[{"content":"이 글은 CPU만으로 돌릴 수 있는 경량 한국어 PII(개인정보) 탐지 모델 Veil-PII-Ko-Lite를 개발한 과정과 결과를 정리한 리포트입니다.\n1. 개발 동기 # 매 프로젝트마다 폐쇄망 환경에서의 PII 모델 적용은 필수 요소. But 아쉬운 한국어 PII 성능 폐쇄망에서 GPU 없이 가볍게 구동할 수 있는 PII 모델 필요 다양한 한국어 PII Category에 대한 탐지 기능 필요 좋은 PII 성능을 보이는 것으로 알려진 모델들을 후보군으로 두고 탐색 후보 내용 적용 한계 BCCard MoAI-Privacy-Filter (2026-08) openai/privacy-filter(1.4B) 한국어 파인튜닝, 29 라벨, Apache 2.0 1.4B 규모로 CPU 서빙 부담. 모델카드가 상업 사용 시 고지를 요구. / 실제 대화체(KDPII)에서는 F1 0.45로 낮은 탐지 성능 FrameByFrame privacy-filter-korean (2026-04) 동일 1.4B 백본, KDPII 학습, 9 라벨 주민번호·카드·사업자번호 등 금융 필수 라벨이 없음. Azure AI Language PII 관리형 API, 한국어 지원 한국어 주소 F1 0.08, 계좌 미검출 등 한국어 PII 탐지 성능 낮음 Microsoft Presidio 규칙 + NER 프레임워크 대체재가 아니라 NER 모델을 끼워 쓰는 프레임워크 개념. 한국어 인식 기능이 있긴 하지만 정형 카테고리 5종만 탐지를 지원함. 전후처리에 특화되어 있어 PII 모델과 패키징에 활용 예정 결론적으로 \u0026ldquo;경량 · 한국어 강함 · 카테고리 넓음 · 라이선스 부담 없음\u0026rdquo; 네 조건을 동시에 만족하는 공개 모델이 없어 직접 개발하기로 하였습니다.\n2. 개발 목표 # 목표 정량기준 결과 정확도 공개 벤치 및 합성데이터 3종의 평가결과가(KDPII test · BCCard validation · 자체 합성 데이터) BCCard PII 모델 이상의 성능을 보일 것 · BCCard의 PII 모델을 목표모델로 설정한 이유는, 해당 모델이 현재 한국어 PII 모델 중 가장 많은 Category를 커버하면서도 높은 성능을 보이기에 (다만 크기가 큼..), 경량 모델로 해당 모델 이상의 성능을 달성하는 것을 목표로 설정하였습니다. 달성 경량성 파라미터 ≤ 150M, 배포 파일 ≤ 150MB, 정확도 손실 없이 INT8 달성 — 110M · 143MB · 무손실 카테고리 BCCard가 제공하는(현재 기준 가장 많음, 29개) 카테고리 수준으로 지원할 것 달성 — 32종 (차량번호·가입번호·단말 S/N 추가) 라이선스 고지 의무 없는 상업 사용 달성 — 백본·데이터 모두 Apache 2.0 3. Backbone 선택 # 작은 크기 및 한국어를 지원하는 PII backbone 모델 5종을 비교분석하였습니다.\n후보 5종을 같은 데이터(2종 - KDPII, BCCard val 데이터), 같은 하이퍼파라미터로 2 epoch로 학습하여 비교하였습니다.\n최종 판단 기준은 F1 Score 기준 정확도 및 모델의 크기를 가장 중요한 요소로 두어 평가하였고, 최종적으로 \u0026lsquo;ko-electra-base-v3\u0026rsquo; 모델을 최종 backbone 모델로 선택하였습니다.\n백본에 대해 PII 탐지(라벨을 인식할 수 있도록)가 가능하도록 동일 조건으로 fine-tuning 진행했고, train loss는 동일하게 떨어졌습니다.\n다만, validation set에 대해서는 아래와 같이 Ko-Electra-base-v3 모델이 가장 좋은 성능을 보였습니다.\n백본 파라미터 라이선스 KDPII test F1 BCCard val F1 판정 monologg/koelectra-base-v3 110M Apache 2.0 0.8789 0.9613 채택 — 가장 작고 KDPII 스코어는 최고 득점 kakaobank/kf-deberta-base 185M MIT 0.8798 0.9661 +0.1~0.5pt 이나 INT8 245MB로 크기 목표 초과 lucid/deberta-v3-base-korean 185M Apache 2.0 0.8533 0.9527 탈락 beomi/KcELECTRA-base 110M MIT KoELECTRA 하회 탈락 (구어체 특화 이점 확인 안 됨) microsoft/mdeberta-v3-base 276M MIT exact 0.39 → 오프셋 보정 후 회복 탈락 — 크기 2.5배, SentencePiece 선행공백 오프셋 문제로 디코더 보정 필요했음 4. 학습 데이터 # 학습셋 구성 (행 수, full 셋 기준 · 총 128k)\n출처 행 수 비고 BCCard 57,851 ko 43k · en 15k KDPII 40,109 문장 + 3,664 대화 합성 (자체) 26,540 템플릿 4.7k × 생성기 막대 길이 = 행 수 비례. 학습 시 반복 가중 ×2를 쓴 출처(BCCard)는 epoch당 실효 256k 행.\n4-1. BCCard/privacy-filter-openpii-masking (CC-BY-4.0) - 약 72,400행 # 데이터 설명 아래 세 가지 출처의 데이터에 대한 통합본으로 확인 openpii-1.5m-ko/none 26,268행 (ai4privacy 영문 데이터의 한국어 번역본) openpii-1.5m-ko/perturbation 16,268행 (위 데이터를 한국 포맷으로 변경, 생성한 데이터) domain/synthesis 11,170행 (BC카드 자체 합성데이터) \u0026lt;데이터 샘플\u0026gt;\n원문 : 연락을 위해 jimin3292@webmail.example.net 또는 공오일 일일구 공육육육 로 문의해 주세요.\n정답 : EMAIL jimin3292@webmail.example.net · PHONE 공오일 일일구 공육육육\n수집 사유\n한국어 PII Category 29개의 라벨을 기준으로, 공개된 가장 큰 데이터이며, 비교 대상(BCCard 모델)과 같은 분포로 학습 후 평가해야 BCCard validation 비교가 공정해지기에, 해당 데이터를 사용하게 되었습니다. 데이터 품질 검수\n학습 이전에, 데이터 품질이 적합한지에 대한 전수 검사를 실시 탐지된 문제점 주민번호·전화·카드·면허·여권·사업자번호 등 21종 포맷 검증기로 행 단위 감사(정규표현식)를 수행했으며, 세 하위 출처 중 openpii-1.5m-ko (ai4privacy 번역본 21k)는 35% 행이 불량이었음 운전면허 라벨의 61%가 0W4SB7XAR4 같은 랜덤 대문자열 이름의 13%가 선임Blattnig 같은 혼합 문자. 한국 포맷으로 변경 전 데이터라, 당연한 결과로 보이며 BC Card에서 해당 데이터를 왜 학습에 포함했는지 이유는 모르겠으나, 해당 데이터 포함 시 모델 성능이 하락하는 것을 확인하였고 포맷검증기에서 확인된 문제 데이터는 모두 제거함. BCCard 모델 Description에서 언급된, 스스로 약하다고 밝힌 ACCOUNT_NUMBER·ZIPCODE·PORT의 원인이 \u0026ldquo;랜덤 문자열 = 식별자\u0026rdquo; 데이터를 제거하지 않고 학습한 결과로 예상. 처리: 불량 행을 제외한 clean 셋과 전체 full 셋을 모두 만들어 절제 실험으로 비교했음(6절). \u0026lt;불량 데이터 예시\u0026gt;\n정상 : DRIVER_LICENSE = 11-23-456789-01 불량 : DRIVER_LICENSE = 0W4SB7XAR4 4-2. KDPII (고려대, CC-BY-4.0) — 약 40,000개 문장 · 3,664 대화 # 데이터 설명 실제 구어체 대화 기반의 유일한 한국어 PII 실데이터입니다. \u0026lt;데이터 샘플\u0026gt;\n원문 : 나 캐나다에서 태어나서 5년 전 17살에 한국 왔어.\n정답 : LCP_COUNTRY 캐나다 · QT_AGE 17살 · LC_ADDRESS 한국 → 매핑 후: ADDRESS 한국 (라벨에서 LCP_COUNTRY와 같은 국가 개념은 잡을 필요없는 데이터이기에 학습데이터에서 제거했음)\n수집 사유\n공식 train/valid/test 분할이 있어 외부 비교 기준이 되며, 문장 평균 25자로 짧은 데이터로 구성되어 있습니다. 처리\n33개의 KDPII category 중 키·몸무게·혈액형·종교 등 우리 모델에서 탐지하지 않는 카테고리들은 비 라벨로 처리하고 우리 모델에서 탐지하는 16개의 라벨로 매핑. 카드·계좌 스팬에 붙은 은행명은 숫자 구간만 남기도록 정규화하여 BCCard 데이터셋의 포맷과 일치시킴. 4-3. 합성 데이터 (자체) — 26,500행 # 데이터 설명 위 수집한 데이터에서 미비된 라벨에 대한 데이터는 gpt-5.4 모델을 활용하여 생성하여 채웠습니다. 데이터는 원본에서 빈칸을 맞추는(PII를 맞추는 위의 데이터들과 같은 형식) 데이터와 빈 \u0026lt;데이터 샘플\u0026gt;\n원문 : 연규도우 과장님 고객님, 문의하신 4918-5036-****-3258 카드 승인번호 55276357 건은 등록 계좌 587918-37-599711 로 1995.07.15 환불됩니다. 요금제 5G 프리미어 80입니다.\n정답 : PERSON 연규도우 · CARD_NUMBER 4918-5036-****-3258 · TRANSACTION_APPROVAL_ID 55276357 · ACCOUNT_NUMBER 587918-37-599711 · DATE 1995.07.15 (뒤의 \u0026ldquo;요금제 5G 프리미어 80\u0026quot;은 삽입된 hard-negative)\n수집 사유\n공개 데이터에 없는 라벨(차량번호·가입번호·단말 S/N)과 부족한 도메인(금융·통신·보험 상담문)을 보강하고, 주문번호·상품코드·금액 같은 hard-negative를 정답과 같은 빈도로 넣어 \u0026ldquo;숫자면 다 잡는\u0026rdquo; 과탐을 억제하는 효과를 주었습니다. 방식\nBC Card에 잘못된 포맷의 데이터가 들어가서 데이터 품질을 떨어뜨렸던 사례를 참고하여, LLM이 아래와 같은 원문을 생성하되, 원문 내에 들어가는 pii 카테고리 데이터에 대해서는 로직 기반으로 채우도록 하여, 환각으로 인한 잘못된 label 데이터가 포함되는 사례를 방지하였습니다. LLM생성 : {PERSON} 고객님, 문의하신 {CARD_NUMBER} 카드 승인번호 {TRANSACTION_APPROVAL_ID} 건은 등록 계좌 {ACCOUNT_NUMBER} 로 {DATE} 환불됩니다.\n위 category별 삽입되는 데이터는 프로그래미틱하게 생성 반영\n5. 학습 # 5-1. 학습방식 → Full Fine-Tuning # Ko-Electra-base-v3 백본 모델의 경우, 기존 transformer encoder에 이진판별(토큰 간 동일여부)이 가능한 헤더가 붙어있었습니다. (768 → 1). 해당 헤더 부분을 PII category 분류 목적에 맞게, 768 → 129dim의 헤더로 변경하였습니다. 그리고, 가중치 업데이트는 전체 모델 architecture에서 기본적으로 Embedding layer, Encoder layer를 학습합니다. (헤더만 학습하는 게 아니라, 전체 레이어에 대해 기울기 계산을 진행하여 백본 전체를 갱신함!!) 그리고 위에서 언급한 토큰 분류헤더에 대해, 각 토큰별로 총 129개 라벨에 대한 확률값을 예측하는 형태로 학습을 진행하였습니다. 라벨이 129개인 이유는, 총 32개의 카테고리 라벨이 있고, 각 라벨별로 \u0026lsquo;시작 B(Begin)\u0026rsquo;, \u0026lsquo;중간 I(Inside)\u0026rsquo;, \u0026lsquo;끝 E(End)\u0026rsquo;, \u0026lsquo;단일 S(그냥 혼자서 하나의 엔티티를 가지는 경우, Single)\u0026rsquo; 총 4가지 정보로 분류되고, 마지막으로 엔티티가 아님에 해당하는 라벨까지 더해서, 32 x 4 + 1 = 129개의 예측해야 할 라벨이 발생합니다. 다만, 모델의 output이 129개 라벨에 대한 토큰별 확률값이고, 각 토큰별 129개의 확률값 중 제일 높은 확률의 라벨을 고르는 방식(argmax)을 선택할 경우, 토큰의 시작-중간-끝 요렇게 문법(?)에 맞게 배치되어야 하는데, 단순 높은 확률값으로만 채택할 경우, 단일 토큰의 예측라벨이 IBBS\u0026hellip;BBI, 뭐 이런 식으로 말도 안 되는 토큰 배치가 발생할 수 있습니다. 이에, Viterbi 알고리즘을 적용하여, 앞단의 토큰의 라벨이 예측되었을 때, 뒷 토큰의 경우 그 자리에 올 수 있는 라벨 중에서 가장 높은 확률을 가진 라벨을 채택하는 방식으로 보정하여 분류하였습니다. Tuning Params Loss Function : nn.CrossEntropyLoss Label Smoothing : 0.05 (v1~v3a) → 0 (v3b·v4) Optimizer : AdamW weight decay : 0.01 dropout : 0.1 learning rate : 3e-5 → 5e-5 (v3b·v4) warmup : 10% epoch : 5→6 Loss 제외 토큰 반영(ignore_index=-100 처리) padding, [CLS], [SEP] 토큰은 학습 제외 5-2. 학습과정 # 버전별 데이터셋\n버전 BCCard 공개 데이터 KDPII 공개 데이터 자체 생성 합계 버전 특이점 v1 57,851 43,504 17,231 118,586 같은 조건으로 두 벌을 학습 - 불량 데이터 제거 분(108,538) vs All(118,586) v2 57,851 43,494 26,540 (+9,309) 127,885 BCCard, KDPII 데이터에는 없는 차량번호, 가입번호, 단말 S/N 데이터를 추가하여 총 합성 데이터를 1.5배로 증강 v3-1 57,851 43,494 26,540 127,885 데이터 변경 없음 / 학습 시, BCCard 부분만 데이터를 x2하여 같은 데이터를 강조 학습 v3-2 57,851 43,494 26,540 127,885 데이터 변경 없음. 학습률, label smoothing, epoch만 조정하여 학습 v4 / 최종 57,851 43,498 26,875 / +335 128,224 합성 데이터 내 라벨 오류 수정 → \u0026ldquo;김철수, 홍길동, 이영희\u0026quot;가 정답 없이 박혀 있던 1,300여 곳을 개인정보로 정정하고, 이름이 문맥에 등장하는 문장 139개 추가 생성 반영 버전별 의도 및 결과\n버전 train set 의도 기대했던 효과 결과 v1 train_clean 108,538 / train_full 118,586 같은 조건에서 불량 행 제외(clean) vs 전체(full) 두 벌 병렬로 학습 진행 불량 데이터 제거한 데이터셋에서 더 높은 스코어 기대.. 반대로 나옴! / 원인은, BC Card 데이터에 불량데이터의 분포가 train, valid set 모두 동일한 분포로 포함되어 있었는데, train에서 제거하여, 해당 패턴을 학습하지 못한 상태에서 validation에서 해당 패턴의 데이터를 맞추지 못한 문제 v2 train_*_v2 127,885 다양한 category에 대한 추가 합성 데이터를 1.5배로 늘려서, 추가 category에 대한 학습 반영 BCCard, KDPII에 없는 라벨(차량번호/가입번호/단말 S/N)을 채워 학습 카테고리 범위 확대 KDPII, BCCard 두 벤치는 성능향상 없었고, 합성 데이터 한정 0.977 달성함. v3a train_full_v2 (v2와 동일) 확인 결과 BCCard 데이터에 대한 성능이 좋지 않았고, 그 부분에 대한 강화 전략 확인 결과, 데이터 자체에 문제는 없어 보여 강조 학습을 진행하면 좀 더 성능 향상을 얻을 수 있지 않을까..? 효과 없음. BCCard val 0.980 (오히려 −0.2pt), KDPII 0.934 동일. 이미 충분히 보고 학습에 반영된 것으로 이해 v3b train_full_v2 (v2와 동일) 데이터 동일, 하이퍼파라미터만 수정 → lr 3e-5→5e-5, ls 0.05→0, epoch 6 학습 파라미터 조정을 통해 최적화\u0026hellip;성능 수렴 지점을 개선해보고자 함 KDPII 0.935 (+0.1pt), BCCard 0.982. loss는 400배 내려갔으나 점수는 사실상 동률 v4 (최종) train_full_v3 128,224 / + 이름 문맥 템플릿 139행, 더미 이름 1,300건 라벨 수정 합성 템플릿에 플레이스홀더 없이 박혀 있던 김철수, 홍길동, 이영희를 자동으로 {PERSON}으로 치환하고, 이름이 문맥에 등장하는 수작업 템플릿 추가 v3b가 이 세 이름을 개인정보로 못 잡는 문제(O로 학습된 것 확인) 해결 벤치는 v3b와 동률(KDPII 0.934 · BCCard 0.983)이나 테스트용 17문장 11/17 → 17/17로 향상. 실사용 성능 개선으로 이해 6. 학습 결과 # exact-match span F1 · 같은 스코어러 · 같은 디코더\n평가셋 Veil-PII-Ko-Lite (110M) BCCard MoAI-PF-INT8 (1.4B) FrameByFrame (1.4B, 9라벨) Azure AI Language PII KDPII test (4,891) .934 .453 .516 .463 BCCard val · ko (10,743) .983 .959 n/a .403 합성 heldout v2 (670) .965 .533 n/a .504 6-0. Veil-PII-Ko-Lite 모델 제공 32개 Label # Veil-PII-Ko-Lite 모델 제공 32개 Label 목록 # 라벨 한글 설명 예시 유형 사람·조직 PERSON 사람 이름 실명·호칭·서명. 성만 있어도 잡는다 김철수 / 이영희 씨 / 홍길동 대리 문맥 ORGANIZATION 기관·회사명 회사·학교·병원·관공서. 상호에 사람 이름이 섞이면 문맥으로 가른다 대한항공 / 김민수 치과 / 강남구청 문맥 USER_ID 계정 아이디 로그인 ID·닉네임·핸들 wtkim89 / @veil_admin 혼합 연락처·위치 ADDRESS 주소 도로명·지번·건물명. 행정구역만 있어도 대상 서울 강남구 테헤란로 152 / 판교빌딩 3층 문맥 EMAIL 이메일 주소 RFC 형식 주소 hong@example.co.kr 정형 PHONE 전화번호 휴대폰·유선·대표번호. 구분자가 없거나 점이어도 잡는다 010-1234-5678 / 02)555-1234 정형 IPADDRESS IP 주소 IPv4·IPv6 192.168.0.11 / 2001:db8::1 정형 MACADDRESS MAC 주소 네트워크 인터페이스 물리 주소 00:1B:44:11:3A:B7 정형 PORT 포트 번호 호스트 뒤에 붙는 서비스 포트 :8443 정형 URL 웹 주소 http(s)·www 로 시작하는 링크 https://veil.example.com/u/1123 정형 ZIPCODE 우편번호 5자리 신 우편번호, 6자리 구 번호 06236 / 135-080 정형 한국 신원 SSN 해외 사회보장번호 미국 SSN 등 국외 신원번호 123-45-6789 정형 RRN 주민등록번호 13자리. 생년월일 + 성별코드 + 검증번호 900101-1234567 정형 FRN 외국인등록번호 주민번호와 같은 자릿수, 성별코드가 5~8 900101-5234567 정형 BUSINESS_ID 사업자등록번호 3-2-5 자리 123-45-67890 정형 PASSPORT 여권번호 M/S/R/O + 8자리 M12345678 정형 DRIVER_LICENSE 운전면허번호 지역코드-연도-일련-검증 11-22-123456-78 정형 CI 연계정보 본인확인기관이 발급하는 88자 해시 식별자 88자 Base64 문자열 정형 IPIN 아이핀 번호 인터넷 개인식별번호 13자리 영숫자 정형 금융 CARD_NUMBER 카드번호 14~19자리. 발급사·BIN·검증번호 구조 4111-1111-1111-1111 정형 CARD_EXPIRY 카드 유효기간 MM/YY 또는 MM/YYYY 12/28 정형 CVC 카드 검증코드 카드 뒷면 3~4자리 123 정형 VIRTUAL_CARD_NUMBER 가상 카드번호 일회용·토큰화 카드번호 실카드와 자릿수 동일 정형 ACCOUNT_NUMBER 계좌번호 은행별 자릿수·구분자가 제각각 110-234-567890 정형 TRANSACTION_APPROVAL_ID 거래 승인번호 결제 승인 시 발급되는 식별자 30123456 정형 기기·가입 IMEI 단말 식별번호 휴대폰 15자리 고유번호 356938035643809 정형 VEHICLE_PLATE 차량번호 지역·분류·한글·일련번호 12가 3456 / 서울 32나 1234 정형 SUBSCRIBER_ID 가입번호 통신·서비스 가입 식별자 가입자 관리번호 혼합 DEVICE_SERIAL 기기 일련번호 셋톱·공유기 등 단말 시리얼 SN-4F2A-90C1 정형 기타 DATE 날짜 생년월일·가입일 등 개인을 특정하는 날짜 1996년 12월 8일 / 2026-09-23 혼합 GENERIC_ID 기타 식별자 유형을 특정하기 어려운 식별자를 받는 자리 주문번호 / 회원번호 혼합 SECRET 비밀값 비밀번호·API 키·토큰 sk-live-9f3a\u0026hellip; 혼합 6-1. 모델별 F1-Score # 평가셋 n Veil INT8 Veil fp32 BCCard 1.4B FrameByFrame Azure PII KDPII test 4,891 0.9327 0.9333 0.4533 0.5156 0.4631 KDPII · 대화 단위 458 0.9420 0.9423 0.4661 — 0.4626 BCCard validation · ko 10,743 0.9825 0.9820 0.9594 — 0.4031 BCCard validation · en 3,781 0.9697 0.9700 0.9653 — 0.5295 합성 heldout v2 670 0.9643 0.9652 0.5328 — 0.5035 FrameByFrame의 경우, 벤치를 측정하지 않은 이유는, 해당 모델이 9개 카테고리밖에 지원하지 않아, 직접적 비교가 힘들어, 제외 처리함. \u0026ldquo;PII의 경우, 오답의 리스크가 크기 때문에, Recall 및 Precision도 중요하며, 제공하는 카테고리가 많아졌기 때문에 카테고리별 스코어도 확인할 필요가 있었습니다.\u0026rdquo;\n6-2. 각 모델별 전체 라벨에 대한 Recall Score # 1) KDPII test set 기준 Recall (라벨 17개 보유) # 라벨 n Veil INT8 Veil fp32 BCCard 1.4B Azure PII FrameByFrame 정규식 ORGANIZATION 467 0.886 0.889 0.006 0.392 0.000 0.000 PERSON 274 0.912 0.909 0.307 0.671 0.792 0.000 ADDRESS 208 0.899 0.899 0.082 0.048 0.447 0.000 PHONE 124 1.000 1.000 1.000 0.927 1.000 0.218 EMAIL 81 1.000 1.000 0.975 1.000 1.000 1.000 CARD_NUMBER 78 1.000 1.000 1.000 0.077 0.000 0.000 ACCOUNT_NUMBER 77 1.000 1.000 0.364 0.000 0.000 0.416 DATE 70 0.971 0.971 0.914 0.943 0.986 0.000 USER_ID 61 1.000 1.000 0.656 0.000 1.000 0.000 VEHICLE_PLATE 52 1.000 1.000 0.000 0.000 0.000 0.000 URL 39 1.000 1.000 0.795 0.974 1.000 0.744 RRN 18 0.944 0.944 1.000 0.278 0.000 0.000 PASSPORT 18 1.000 1.000 0.889 0.000 0.000 0.278 DRIVER_LICENSE 17 1.000 1.000 1.000 0.000 0.000 0.000 IPADDRESS 11 1.000 1.000 0.909 1.000 1.000 0.273 FRN 9 1.000 1.000 0.000 0.000 0.000 0.000 micro 1,604 0.9370 0.9370 0.3797 0.4358 0.4333 0.1103 2) BCCard-val-ko set 기준 Recall (라벨 29개 보유) # 라벨 n Veil INT8 Veil fp32 BCCard 1.4B Azure PII 정규식 PERSON 8,214 0.983 0.980 0.969 0.522 0.000 DATE 7,910 0.997 0.997 0.998 0.757 0.000 ADDRESS 7,201 0.991 0.992 0.932 0.171 0.000 EMAIL 4,535 1.000 1.000 0.968 0.528 0.531 PHONE 3,723 0.965 0.965 0.937 0.528 0.154 CARD_NUMBER 2,851 1.000 1.000 0.999 0.002 0.000 ZIPCODE 2,608 0.995 0.995 0.996 0.000 0.000 GENERIC_ID 2,021 0.905 0.903 0.842 0.000 0.000 SSN 1,816 0.958 0.958 0.958 0.000 0.000 RRN 1,743 0.998 0.998 0.997 0.332 0.000 DRIVER_LICENSE 1,723 0.911 0.910 0.867 0.000 0.000 PASSPORT 1,475 0.999 0.999 0.920 0.000 0.002 ORGANIZATION 467 0.981 0.981 0.904 0.559 0.000 IPADDRESS 241 1.000 1.000 1.000 0.996 0.373 TRANSACTION_APPROVAL_ID 233 1.000 1.000 1.000 0.000 0.000 USER_ID 225 0.996 0.996 0.987 0.000 0.000 ACCOUNT_NUMBER 210 0.952 0.952 0.929 0.000 0.409 BUSINESS_ID 207 0.961 0.961 0.942 0.000 0.000 URL 164 1.000 1.000 0.988 0.848 0.305 CI 163 1.000 1.000 0.994 0.000 0.000 PORT 163 0.932 0.932 0.896 0.000 0.000 SECRET 158 0.987 0.987 0.981 0.000 0.000 IPIN 151 0.987 0.987 0.980 0.000 0.000 MACADDRESS 148 1.000 1.000 1.000 0.000 0.277 FRN 120 1.000 1.000 0.992 0.000 0.000 IMEI 106 1.000 1.000 1.000 0.000 0.000 CARD_EXPIRY 104 1.000 1.000 1.000 0.000 0.000 VIRTUAL_CARD_NUMBER 102 1.000 1.000 1.000 0.000 0.000 CVC 92 1.000 1.000 0.978 0.000 0.000 micro 48,874 0.9829 0.9823 0.9589 0.3497 0.0665 3) 자체 합성 데이터 set 기준 Recall (라벨 32개 보유) # 라벨 n Veil fp32 BCCard 1.4B Azure PII PERSON 208 0.947 0.827 0.870 ORGANIZATION 161 0.981 0.074 0.509 PHONE 157 0.994 0.987 0.796 DATE 150 0.987 0.800 0.767 ADDRESS 126 1.000 0.706 0.444 EMAIL 113 1.000 0.982 0.761 USER_ID 41 1.000 0.244 0.000 SECRET 40 1.000 0.650 0.000 IPADDRESS 38 1.000 1.000 0.974 SUBSCRIBER_ID 36 0.972 0.000 0.000 ACCOUNT_NUMBER 35 1.000 0.686 0.000 URL 33 1.000 0.818 0.970 GENERIC_ID 32 0.781 0.313 0.000 DEVICE_SERIAL 32 0.969 0.000 0.000 VEHICLE_PLATE 29 1.000 0.000 0.000 ZIPCODE 28 1.000 0.464 0.000 PASSPORT 27 1.000 0.963 0.000 TRANSACTION_APPROVAL_ID 27 1.000 0.259 0.000 RRN 26 0.923 0.962 0.808 CI 26 1.000 0.808 0.000 FRN 22 1.000 0.000 0.000 VIRTUAL_CARD_NUMBER 22 0.909 0.227 0.000 PORT 21 0.952 0.048 0.000 IMEI 21 1.000 0.524 0.000 DRIVER_LICENSE 20 0.950 0.700 0.000 BUSINESS_ID 19 1.000 0.263 0.000 MACADDRESS 19 1.000 0.737 0.000 IPIN 18 1.000 0.000 0.000 CVC 17 0.941 0.294 0.000 SSN 15 1.000 1.000 0.000 CARD_NUMBER 15 0.933 1.000 0.933 CARD_EXPIRY 12 1.000 0.000 0.000 micro 1,586 0.9786 0.6122 0.4723 6-3. 각 모델별 전체 라벨에 대한 Precision Score # 1) KDPII test set 기준 Precision (라벨 17개 보유) # 라벨 n Veil INT8 Veil fp32 BCCard 1.4B Azure PII FrameByFrame 정규식 ORGANIZATION 467 0.914 0.912 0.375 0.518 0.000 0.000 PERSON 274 0.877 0.886 0.273 0.570 0.670 0.000 ADDRESS 208 0.858 0.858 0.110 0.345 0.830 0.000 PHONE 124 1.000 1.000 0.855 0.412 1.000 1.000 EMAIL 81 1.000 1.000 0.975 1.000 1.000 1.000 CARD_NUMBER 78 1.000 1.000 0.709 1.000 0.000 0.000 ACCOUNT_NUMBER 77 1.000 1.000 0.966 0.000 0.000 0.244 DATE 70 0.919 0.919 0.762 0.229 1.000 0.000 USER_ID 61 0.984 0.984 0.952 0.000 0.984 0.000 VEHICLE_PLATE 52 0.981 0.981 0.000 0.000 0.000 0.000 URL 39 1.000 1.000 0.838 0.974 1.000 0.744 RRN 18 1.000 1.000 0.667 0.833 0.000 0.000 PASSPORT 18 1.000 1.000 0.889 0.000 0.000 1.000 DRIVER_LICENSE 17 1.000 1.000 0.809 0.000 0.000 0.000 IPADDRESS 11 1.000 1.000 0.909 1.000 1.000 1.000 FRN 9 0.900 0.900 0.000 0.000 0.000 0.000 micro 1,604 0.9284 0.9295 0.5623 0.4940 0.6364 0.6189 2) BCCard-val-ko set 기준 Precision (라벨 29개 보유) # 라벨 n Veil INT8 Veil fp32 BCCard 1.4B Azure PII 정규식 PERSON 8,214 0.984 0.980 0.975 0.520 0.000 DATE 7,910 0.996 0.996 0.996 0.815 0.000 ADDRESS 7,201 0.988 0.989 0.931 0.175 0.000 EMAIL 4,535 0.999 0.999 0.968 0.877 0.883 PHONE 3,723 0.965 0.965 0.937 0.250 0.892 CARD_NUMBER 2,851 0.997 0.997 0.996 0.114 0.000 ZIPCODE 2,608 0.995 0.995 0.992 0.000 0.000 GENERIC_ID 2,021 0.885 0.884 0.833 0.000 0.000 SSN 1,816 0.968 0.967 0.958 0.000 0.000 RRN 1,743 0.999 0.999 0.997 0.903 0.000 DRIVER_LICENSE 1,723 0.922 0.921 0.873 0.000 0.000 PASSPORT 1,475 0.999 0.999 0.919 0.000 1.000 ORGANIZATION 467 0.985 0.985 0.944 0.163 0.000 IPADDRESS 241 1.000 1.000 0.992 0.988 0.978 TRANSACTION_APPROVAL_ID 233 0.996 0.996 1.000 0.000 0.000 USER_ID 225 0.996 0.996 1.000 0.000 0.000 ACCOUNT_NUMBER 210 0.995 0.995 1.000 0.000 0.029 BUSINESS_ID 207 0.971 0.971 0.970 0.000 0.000 URL 164 0.988 0.988 0.982 0.837 0.301 CI 163 1.000 1.000 0.994 0.000 0.000 PORT 163 0.950 0.950 0.936 0.000 0.000 SECRET 158 1.000 1.000 1.000 0.000 0.000 IPIN 151 0.987 0.987 0.993 0.000 0.000 MACADDRESS 148 1.000 1.000 1.000 0.000 1.000 FRN 120 1.000 1.000 0.984 0.000 0.000 IMEI 106 1.000 1.000 0.973 0.000 0.000 CARD_EXPIRY 104 1.000 1.000 1.000 0.000 0.000 VIRTUAL_CARD_NUMBER 102 0.971 0.971 1.000 0.000 0.000 CVC 92 0.968 0.968 1.000 0.000 0.000 micro 48,874 0.9822 0.9816 0.9599 0.4759 0.3880 3) 자체 합성 데이터 set 기준 Precision (라벨 32개 보유) # 라벨 n Veil fp32 BCCard 1.4B Azure PII PERSON 208 0.791 0.619 0.694 ORGANIZATION 161 0.975 0.600 0.506 PHONE 157 0.987 0.901 0.316 DATE 150 0.987 0.645 0.567 ADDRESS 126 0.984 0.315 0.409 EMAIL 113 1.000 0.982 0.761 USER_ID 41 0.976 0.556 0.000 SECRET 40 1.000 0.867 0.000 IPADDRESS 38 1.000 0.927 0.974 SUBSCRIBER_ID 36 0.921 0.000 0.000 ACCOUNT_NUMBER 35 0.972 0.558 0.000 URL 33 1.000 0.818 0.970 GENERIC_ID 32 1.000 0.115 0.000 DEVICE_SERIAL 32 0.969 0.000 0.000 VEHICLE_PLATE 29 0.967 0.000 0.000 ZIPCODE 28 0.966 0.076 0.000 PASSPORT 27 1.000 0.377 0.000 TRANSACTION_APPROVAL_ID 27 1.000 0.184 0.000 RRN 26 0.960 0.207 0.750 CI 26 1.000 0.778 0.000 FRN 22 0.917 0.000 0.000 VIRTUAL_CARD_NUMBER 22 0.952 0.385 0.000 PORT 21 0.909 1.000 0.000 IMEI 21 0.955 1.000 0.000 DRIVER_LICENSE 20 1.000 0.119 0.000 BUSINESS_ID 19 0.905 1.000 0.000 MACADDRESS 19 1.000 1.000 0.000 IPIN 18 0.818 0.000 0.000 CVC 17 1.000 0.417 0.000 SSN 15 1.000 0.263 0.000 CARD_NUMBER 15 0.933 0.190 0.933 CARD_EXPIRY 12 1.000 0.000 0.000 micro 1,586 0.9487 0.4716 0.5392 6-4. 양자화 및 Latency # Dynamic INT8로 양자화를 진행한 경우, 모델크기는 113까지 감소했으나, activation까지 양자화(정수)되면서 recall이 대폭 하락했습니다. 원인은 129개 라벨의 1,2등의 prediction score가 매우 근소한 차이라, 성능하락이 컸습니다. 이에, weight는 int8로 양자화하고, activation은 fp32를 유지하여 성능은 유지하고, 모델크기는 143MB로 축소했습니다. 변형 크기 KDPII F1 4스레드 128 / 512 tok 16스레드 512 tok 판정 fp32 ONNX 450MB 0.9339 77 / 274 ms 118 ms 기준 동적 INT8 113MB ~0.80 25 / 95 ms 58 ms 재현율 붕괴, 미채택 weight-only INT8 + fp16 emb 143MB 0.9342 61 / 237 ms 108 ms weight-only INT4 + fp16 emb 100MB 0.9321 67 / 261 ms 119 ms 실험값, 미공개 6-5. 종합 비교 # 항목 Veil-PII-Ko-Lite BCCard MoAI-PF FrameByFrame Azure AI Language PII 최초 공개 2026-09 2026-08 2026-04 2021 GA 파라미터 · 배포 크기 110M · 143MB 1.4B · INT8 수 GB 1.4B · fp32 5.6GB — (API) 라벨 수 32 29 9 13 KDPII test F1 0.934 0.453 0.516 0.463 BCCard val(ko) F1 0.983 0.959 — 0.403 폐쇄망 배포 가능 (CPU ONNX) 가능(but, 무거움) 가능(but, 무거움) - 라이선스 Apache 2.0 Apache 2.0 + 고지 요구 Apache 2.0 - 7. 최종 결과 분석 # 7-1. 최종 결과 # CPU 환경에서 운용이 가능한 110M의 작은 PII 모델 확보 1.4B에 달하는 모델 대비 더 많은 Category를 지원(32개) 경쟁 모델 대비 작은 크기(10배)로 더 높은 탐지 성능 확보 7-2. 매우 작은 모델로도 높은 성능을 낼 수 있었던 이유 # PII detection 시, 비교모델(bccard)의 경우 autoregressive하게 생성하여 output을 도출하는 방식인데, veil 모델의 경우, 각 토큰에 PII 라벨을 찍는 token classification 방식으로 탐지합니다. 즉, veil에서 필요한 출력은 {start, end, label}로 단순하나, 비교모델은 문장을 생성한 뒤, 원문에 다시 맞추는 alignment 과정이 필요한데, 이 부분에서 mask token 길이가 원문과 다르거나, 조사, 공백, 어순이 틀어지는 현상으로 인해 낮은 성능을 보일 수밖에 없습니다. openpii-1.5m-ko 데이터 전수검사를 통해 약 30%가량의 불량데이터를 검출하여 제거하여, 학습 노이즈를 최소화 실제 대화체에 개인정보가 포함되는 형태의 학습데이터를 대거 추가하여, 구어체의 긴 문장이 포함된 벤치에서도 높은 성능을 보임. 합성데이터를 LLM으로 형태만 맞추어 무작위 생성하지 않고, 각 데이터별 포맷 규칙(프로그래미틱하게 작성)하에 작성하여 최대한 리얼월드 데이터와 유사하게 생성하고, 할루시네이션 가능성을 차단하였습니다. 예) Luhn 알고리즘 활용하여, 실제 카드번호와 동일한 형태로 생성 → ISO/IEC 7812 — 앞 6자리 BIN(발급사) + 계좌번호 + 마지막 체크디지트 7-3. 추후 보완사항 # 새롭게 추가된 카테고리 중, S/N 기기단말번호 또는 가입번호와 같은 회사마다/도메인마다 정형화된 포맷이 아니라, 예측이 어려운 부분에 대해서도 학습에 추가했으나, 실제로 예측하지 못하는 케이스가 더러 발생했다. 비정형 타입의 카테고리의 경우, 전후 맥락 및 토큰의 위치정보 등을 통해 최대한 예측했으나, 아래 실제 테스트 결과와 같이 아직 탐지하지 못하는 케이스들이 확인되었고, 전체 카테고리의 탐지성능을 균일하게 끌어올리는 것이 추후 과제로 보여진다. (물론 실제 고객 환경에 적용 시, 해당 문제가 발생할 경우 모델단이 아닌 presidio를 통한 후처리를 통해 domain specific한 부분들은 어느 정도 처리가 가능하긴 함) 마지막으로, 실제 기업 내부 프로덕션에 올려서 실 데이터 기반으로 검증이 필요함. 입력 결과 평가 가입번호: KT469200698 (학습 포맷) SUBSCRIBER_ID 1.00 정답 계약번호 884102993317 SUBSCRIBER_ID 0.999 정답 가입번호: SKT-99-AB-77312 MACADDRESS 0.49 위치는 잡았으나, 라벨 오답 증권번호 HW-LIFE-2024-88231 GENERIC_ID 0.59 위치는 잡았으나, 라벨 오답 단말 S/N RF5IC1T7P97 DEVICE_SERIAL 1.00 정답 공유기 시리얼 넘버 QX-88Z-41772-KR MACADDRESS 0.998 문맥이 있어도 틀림\u0026hellip; 884102993317 RRN 0.985 문맥이 없어 완전 틀려버림.. 8. 사용방법 # HuggingFace https://huggingface.co/1T/veil-pii-ko-lite DockerHub → 모델만 담아 패키징한 이미지와 presidio 내에 모델을 담아 패키징한 이미지를 아래 dockerhub에 올려두었습니다. https://hub.docker.com/repository/docker/zzang9680/veil-pii/tags PlayGround https://20-249-59-7.sslip.io/ 직접 여러 모델과 비교해서 실행해보실 수 있게 플레이그라운드를 호스팅해두었습니다. 해당 플레이그라운드에서는 아래 4개 모델의 PII 성능을 비교 확인하실 수 있게 구성해두었습니다. Veil-PII-Ko-Lite (우리 모델) MoAI-Privacy-Filter (BCCard 모델) FrameByFrame Azure AI Language PII ","date":"2026.09.28","externalUrl":null,"permalink":"/posts/veil-pii-ko-lite/","section":"글","summary":"","title":"Veil-PII-Ko-Lite — 경량 한국어 PII 모델 개발 리포트","type":"posts"},{"content":"오늘은 최근 Fine-Tuning 시 가장 많이 사용하는 매서드중 하나인 LoRA에 대해서 정리해보도록 하자.\n먼저 LoRA는 PEFT로 대표되는 Fine-Tuning 학습 방법이고,\n최근 LoRA에서 파생된, QLoRA, MoRA 등 매서드들이 소개되면서, Fine-Tuning의 주류로 자리잡고있다.\nLoRA를 비롯한 최근의 Fine-Tuning 방법들에 대해서 이해하기 위해서 모태가 된 PEFT 방식이\n왜 각광받게 되었는지 먼저 알아볼 필요가 있다.\n1. DonwStream Task에 Fine-Tuning 하기에 모델이 너무 커지고 있다\u0026hellip; # 몇년전부터, 대부분의 언어 모델이 규모의 경쟁으로 돌입했고,\n현재도 여전히 규모를 통해 성능을 끌어올린다 라는 공식에는 변함이 없다.\n물론 엄청난 규모의 자원과 데이터를 통한 학습이 SOTA 모델의 성능을 끌어올리고 있는것은 맞지만,\n실제 다양한 도메인에서 요구하는 퀄리티를 만족시키기 위해서는 모델의 Downstream Task Fine-Tuning은 필수적이다.\n하지만, 쓸만한 모델은 모두 최소 8B이상이 되어야하는 현실에서,\n8B모델을 Load하는데만, float32기준으로 32GB, Full Fine-Tuning 진행 시, 보통 2~3배정도 소요되기때문에,\n현재 2~3000만원 정도하는 A100 GPU 한장으로도 학습이 쉽지 않다.(Quantization은 일단 배제하고 얘기하자.)\n2. Catastrophic Forgetting (파괴적 망각) # Fine-Tuning을 진행할때, weight update가 이루지면서, 어쩌면 기존 학습 내용중 일부를 망각하는것은 자연스러운 현상이다.\n다만, 이것을 최소화 하기위해서는 일반화된 기존의 학습데이터와 새롭게 학습해야하는 Downstream task data가\n균형있게 구성되어야하고, 잘 만들어진 데이터로 학습해야, 이 현상을 최소화 할 수 있는데,\n데이터 생성 비용자체가 매우 많이 들기때문에, 쉽지 않다. 이때문에 Fine-Tuning 진행 시, 기존 성능을 잘 유지하느것이\n가장 큰 노하우 이자 관건이 된다.\n이러한 이유들로 인해, LLM의 Fine-Tuning 과정에서 적은 리소스를 활용하면서도 성능 하락을 방지할 수 있는 학습 방법론에\n대한 필요성이 대두되었다.\nPEFT는 이 문제들을 어떻게 해결했나?\u0026hellip; # 최초에 model을 학습한다는것의 의미는 전체 모델 weight를 무작위로\nupdate하며, 목표에 적합한 형태를 띄도록 변화해나가는것을 의미한다.\n반면, Fine-Tuning을 진행할때는, 최초 학습된 어느정도 최적화된 모델의 weight를 추가적으로 update,\n미세 조정의 개념으로 학습을 진행하게 되는데,\n여기서, weight를 어떤식으로 최적화나 업데이트할지, 어떤 weight를 freezing할지는 선택한 fine-tuning방식에 따라 결정된다.\n자, 그럼 PEFT는 어떤 학습 방법으로 이 문제들을 해결하고자 했을까?\n[💡 핵심 IDEA]\n\u0026ldquo;Pre-Trained Model 대부분의 parameter를 freezing하고 일부 prameter만 파인튜닝한다\u0026rdquo;\nPEFT의 아이디어를 한마디로 표현하면 위와 같은데,\n너무나 당연한 설명이라, 잘 와닿지 않는다.\n물론, 위와 같이 학습하게 된다면,\n➡️ 일부 파라미터만 Update하기 때문에, 계산비용과 GPU 리소스는 줄어들 수 밖에없다.\n➡️ 일부 파라미터만 Update하기 때문에, 기존 학습 weight 정보를 보존할 수 있다.\n➡️ 추가로, 일부 파라미터에 대한 약간의 수정내용만 발생하기 때문에, 학습파일도 Mb단위로 저장이 가능하다.\n설명한대로라면 간단히 문제들을 해결할 수 있어 보이는데,\n구체적으로 어떤 방식을 통해 효과적으로 문제를 해결했는지 자세히 알아볼 필요가 있다.\n오늘은 PEFT으로 대표되는 학습 방법론중에서도 LoRA를 통해 그 방법을 활용해보고자 한다.\nLoRA는 어떻게 동작하는가? # * LoRA\narXiv 2021 | Edward J. Hu, Yelong Shen, Phillip Wallis, Zeyuan Allen-Zhu, Yuanzhi Li, Shean Wang, Lu Wang, Weizhu Chen at Microsoft Corporation\n17 Jun 2021\n- Paper : https://arxiv.org/abs/2106.09685\n- Github : https://github.com/microsoft/LoRA\n먼저 LoRA는 \u0026ldquo;Low-Rank Adaptation of Large Language Models\u0026quot;의 약자를 의미한다.\n여기서 Low-Rank Adaptation 이 LoRA의 모든것을 표현한 문장이다.\nLoRA를 좀 더 이해하기 쉽게, 직접적으로 풀어쓴다면 이렇게 설명할 수 있을 것 같다.\n\u0026ldquo;기존 weight parameter들은 freezing한 후,\n별도로 구분된 저차원의 레이어(adapter, 이하 어댑터)를 학습하고, 이 어댑터를 통과한 값과,\n기존 Pre-Trained model을 통과한 값을 더함(+)으로써 미세하게 조정내용을 반영하는 방법\u0026rdquo;\nLoRA Adapter 구조\n위 그림은 LoRA가 적용, 학습되는 구조를 표현한 그림이다.\n그림을 보면서 위 설명을 다시 한번 읽어보면 좀 더 이해가 잘 될것이다.\n그리고, LoRA는 현재 PEFT 라이브러리에서 lora_config클래스를 통해\n손쉽게 구현이 가능하지만, 내부 학습 방식을 좀 더 상세하게 이해하기 위해서는 코드레벨로 이해해보는것이 필요하다.\n아래는 LoRA의 학습 Architecture를 code로 직접 구현한 내용이다.\nclass LoRA(nn.Module): def __init__(self, model, r=8, alpha=16, dropout_rate=0.1): super(LoRA, self).__init__() self.model = model self.r = r self.alpha = alpha self.scaling = alpha / r self.dropout_rate = dropout_rate # 드롭아웃 비율 추가 # 원본 모델 가중치 고정 for param in self.model.parameters(): param.requires_grad = False # LoRA 레이어를 어텐션 프로젝션에 주입 for name, module in self.model.named_modules(): if isinstance(module, RobertaSelfAttention): # 쿼리, 키, 밸류 프로젝션 수정 self._inject_lora(module) def _inject_lora(self, module): for proj in [\u0026#34;query\u0026#34;, \u0026#34;key\u0026#34;, \u0026#34;value\u0026#34;]: weight = getattr(module, proj).weight bias = getattr(module, proj).bias out_features, in_features = weight.shape # LoRA 레이어 생성 lora_A = nn.Linear(in_features, self.r, bias=False) lora_B = nn.Linear(self.r, out_features, bias=False) nn.init.normal_(lora_A.weight, std=0.02) nn.init.zeros_(lora_B.weight) # LoRAProjection에 드롭아웃 비율 전달 lora_proj = LoRAProjection(weight, bias, lora_A, lora_B, self.scaling, dropout_rate=self.dropout_rate) setattr(module, proj, lora_proj) def forward(self, *args, **kwargs): return self.model(*args, **kwargs) class LoRAProjection(nn.Module): def __init__(self, weight, bias, lora_A, lora_B, scaling, dropout_rate=0.0): super(LoRAProjection, self).__init__() self.weight = weight self.bias = bias self.lora_A = lora_A self.lora_B = lora_B self.scaling = scaling # 드롭아웃 레이어 추가 if dropout_rate \u0026gt; 0.0: self.dropout = nn.Dropout(p=dropout_rate) else: self.dropout = None def forward(self, x): # LoRA 부분 계산 lora_out = self.lora_A(x) if self.dropout is not None: lora_out = self.dropout(lora_out) lora_out = self.lora_B(lora_out) # 원본 출력과 LoRA 보정값의 합산 return nn.functional.linear(x, self.weight, self.bias) + self.scaling * lora_out 자, 이제 그림과 위 코드를 뜯어 보면서 LoRA 학습 진행과정을 차례차례 살펴보자.\n(* 이제 위 그림에서 주황색 마주보고있는 사다리꼴을 어댑터로 표현하겠다.)\n0) Parameter Settings # def __init__(self, model, r=8, alpha=16, dropout_rate=0.1): super(LoRA, self).__init__() self.model = model self.r = r self.alpha = alpha self.scaling = alpha / r self.dropout_rate = dropout_rate # 드롭아웃 비율 추가 위는 LoRA 클래스 내부에서 LoRA 학습 옵션을 정의하는 부분이다.\n먼저 기존 Pre-trained Model을 \u0026lsquo;model\u0026rsquo;인자로 상속받게 된다.\n그리고 여기서 r(rank), alpha 인자가 LoRA의 핵심 인자값이 된다.\n(1) R (Rank)\n먼저 rank는 LoRA에서 저차원 행렬의 차원을 결정하는 주요 파라미터다.\nrank는 위 그림의 어댑터에 해당하는 A, B weight matrix의 이음새 부분에 해당하는 차원을 셋팅하는 부분이다.\nrank는 A, B weight matrix의 차원을 결정지으며,\n크기가 작을 수록, 학습 시간과 리소스 사용량, 학습할 수 있는 표현의 다양성(복잡성)은 감소한다.\n반대로 크기가 클수록, 학습 시간, 리소스 사용량, 학습할 수 있는 표현의 다양성은 증가한다.\n조금 더 자세한 내용은 아래 adapter 설명 부분에서 참고하자.\n(2) Alpha\nalpha는 학습률과 관련된 스케일링 파라미터로, 저차원 행렬로부터 얻은 변화량을 조절하는데 사용된다.\nLoRA는 저차원에서 학습한 보정 값을 A x B 로 표현하는데, 이 보정 값을 그대로 사용하지 않고, \u0026lsquo;alpha / rank\u0026rsquo; 로 스케일링해서 적용한다\n목적 : LoRA에서 학습되는 보정값을 너무 크게 적용하지 않도록, 모델이 안정적으로 학습할 수 있도록 저차원 보정값의 크기를 조절하는것을 목적으로 하며, 이를 통해 저차원의 효과로 이뤄지는 모델의 보정이 너무 크거나 작지 않도록 가중치 업데이트의 크기를 제어하는 역할을 한다. 효과 : 학습의 안정성과 수렴 속도를 높이고, 저차원에서 얻은 정보를 적절하게 원래의 가중치에 반영할 수 있게 된다. 0) Origin model weight parameter Freezing # # 원본 모델 가중치 고정 for param in self.model.parameters(): param.requires_grad = False 당연하게도, 새로 추가된 LoRA 파트가 아닌, 기존모델의 parameter는 update되지 않도록 freezing 처리한다.\n1) LoRA Adapter Input # 먼저, LoRA의 어댑터에 어떤값을 input vector[x]로 사용해야하는지 궁금할 것이다.\n결론적으로 input vector는 \u0026ldquo;last_hidden_state\u0026quot;를 사용하는것이 일반적이다.\nlast_hidden_state을 사용하여, adapter를 학습하는 이유는,\n대부분의 트랜스포머 기반 모델에서, last_hidden_state는\ninput sequence가 여러 layer층을 거치면서, 가장 풍부하고 고차원적인 표현을 담고 있는\n최종적인 학습 정보이므로, last_hidden_state에 미세조정을 할때 가장 효과적이기 때문이다.\n2) Pre-Trained Weights \u0026amp; Adapter # (1) Pre-Trained Weights\n먼저 input vector는 기존 weight parameter를 통과하게 되면서,\n기존의 가중치 행렬을 적용받은 output A를 뱉는다.\n단순히 pre-trained된 가중치 행렬인 Linear layer를 통과하는 과정을 의미한다.\n(2) Adapter\n먼저 weight parameter가 update되기 전 최초의 adapter\n는 위 수식과 같이, 평균이 0, 분산이 σ2 인 정규분포값을 최초값(initialization)을 가진다.\n최초에는 제로 행렬로 초기화 되어있지만, backpropagation을 반복하면서, 값이 update되게 된다.\n여기서, 하단의 가중치 행렬을 A, 윗단의 가중치 행렬을 B라고할때,\nA행렬에서 고차원에서 저차원으로 투사되면서, 모델은 \u0026lsquo;중요한 특징\u0026rsquo;들만 선택적으로 학습을 진행한다.\n고차원의 데이터를 간소화 하며, 정보의 중요한 부분만 추출해 학습하는 과정을 거친다.\n이어서, B행렬을 통과하며, 저차원에서 고차원으로 다시 투사하는 과정에서 중요한 정보들이 고차원 공간에\n반영되면서 최종적으로 성능이 개선되게끔 학습된다.\nLoRA의 가중치 업데이트 공식은 아래와 같다.\nW**′ :** 업데이트 된 가중치 행렬 A : rank x d 크기의 저차원 행렬을 의미한다. (저차원으로의 투사) B : d x rank 크기의 저차원 행렬을 의미한다. (저차원에서 고차원으로 다시 투사) Alpha : 보정값의 크기를 결정하는 파라미터로, alpha값이 클수록 보정값이 더 많이 반영된다고 이해하면 된다. 결국 rank설정값은 A와 B의 행렬의 크기를 결정하게 된다.\n이 Rank개념에 대해 논문에서는 4, 8차원에서 가장 일반화된 성능을 가지고오며, 성능과 리소스, 속도 측면의 trade-off관계의\n적정성을 가진다고 설명하고 있다.\n쉽게말해,\nRank를 크게 설정하면, 좀 더 복잡한 문맥정보를 학습하겠지만,\nLoRA의 장점인 빠른 학습 속도 및 낮은 리소스 사용량이라는 장점을 희생해야하고,\nRank를 작게 설정할 수록 장점은 극대화 되지만, 복잡한 문맥정보를 학습하는데 한계가 있다는 점이 있기 때문에,\n잘 조정해서 활용해야한다.\n여담이지만, LoRA는 이같은 특징때문에, 반복적인 fine-tuning에서 추가적인 성능향상이 어렵다는 단점을 가지고 있고,\n최근 이를 보완해서 input, output 차원이 동일한 mora라는 개념이 등장하기도 했다.\n자, 그러면 LoRA Adapter가 실제로 학습 되는 부분을 살펴보도록 하자.\n먼저 LoRA가 학습되는 파트에 대한 클래스(\u0026lsquo;LoRAProjection\u0026rsquo; : adapter 부분에 해당)를 구현한 후,\n해당 클래스를 LoRA 클래스 내부에 끼워넣게 된다.\nclass LoRAProjection(nn.Module): def __init__(self, weight, bias, lora_A, lora_B, scaling, dropout_rate=0.0): super(LoRAProjection, self).__init__() self.weight = weight self.bias = bias self.lora_A = lora_A self.lora_B = lora_B self.scaling = scaling # 드롭아웃 레이어 추가 if dropout_rate \u0026gt; 0.0: self.dropout = nn.Dropout(p=dropout_rate) else: self.dropout = None def forward(self, x): # LoRA 부분 계산 lora_out = self.lora_A(x) if self.dropout is not None: lora_out = self.dropout(lora_out) lora_out = self.lora_B(lora_out) # 원본 출력과 LoRA 보정값의 합산 return nn.functional.linear(x, self.weight, self.bias) + self.scaling * lora_out 위는 실제 adapter의 학습 방식을 표현하는 부분이다.\nnn.functional.linear(x, self.weight, self.bias) + self.scaling * lora_out 기존의 weight, bias가 적용된 linear matrix의 output을 lora output와 더해서,\nlora adapter로 도출된 내용을 통해 미세조정을 적용받게 되는것을 확인할 수 있다.\n(3) 학습된 weight의 병합\n마지막으로, 1번과 2번의 결과로 도출된 weight를 더하게되는데,\n\b이것은, 만약 곱셈으로 처리될 경우, vector 공간에서 전체 weight의 방향과 위치 자체가 update되어,\n기존의 학습 정보가 크게 틀어지게된다.\n말그대로, substream task에 대한 \u0026lsquo;미세조정\u0026rsquo;의 목적을 가진 방법이기 때문에,\n덧셈을 통해, 위치 조정의 효과를 보기 위해 덧셈을 통해 처리하게 된다.\n오늘은 최근 Fine-Tuning의 대세가 된, LoRA의 구조 및 구체적인 학습 방식에 대해서 살펴보았다.\n코드레벨의 동작 방식을 살펴보면서 LoRA가 구체적으로 어떤 학습방식을 통해,\n망각현상과 리소스 부하를 감소하는 효과를 내었는지 이해할 수 있었다.\n다음에는 이러한 LoRA의 장점을 살려서, Embedding Model(RoBERTa)을 LoRA를 통해\n직접 Fine-tuning했던 과정과 결과에 대해서 정리해보도록 하겠다.\n[Reference] # https://arxiv.org/abs/2106.09685\nhttps://github.com/microsoft/LoRA\nhttps://www.databricks.com/kr/blog/efficient-fine-tuning-lora-guide-llms\nhttps://4n3mone.tistory.com/7\n","date":"2024.10.18","externalUrl":null,"permalink":"/posts/102/","section":"글","summary":"","title":"[PEFT] LoRA(Low-Rank Adaptation of Large Language Models) Fine-Tuning 학습 방식 뜯어보기! (내부 학습 코드 해석)","type":"posts"},{"content":"Github 구현 코드 먼저 보기 : https://github.com/WontaeKim89/embedding_SimCSE_LoRA\n오늘은 LoRA를 이용해서 Embedding Model을 학습하는 과정을 정리해보려 한다.\n구현코드 링크를 위에 첨부해두었는데,\n먼저 내용 이해에 도움이 되는 내용들을 아래에 정리해두었으니, 포스팅 내용을 쭉 읽어보신 후,\n위 링크를 통해 구현 코드를 살펴보시는걸 권장드린다.\n임베딩 모델은 추천시스템, 검색, 챗봇, 번역, 감정분석, Text Summarization등\n거의 모든 NLP 분야에서 사용되는 매우 중요한 언어모델 분야중 하나이다.\n최근 워낙 생성 모델쪽으로 관심이 집중되어있고, 관련 연구도 훨씬 활발하긴 하지만, 임베딩 모델도 꾸준히 발전하고 있다.\n임베딩 모델은 아직까지는 BERT계열이 많이 사용되고있는듯 하나, 최근 LLM을 활용한 임베딩 모델인 LLM2Vec와 같이,\n디코더 기반의 모델을 활용한 임베딩 모델이 SOTA 리더보드 상단을 차지하고 있다.\n다만, 이러한 LLM기반의 임베딩 모델은 LLM의 특성 상, dimension이 4,096이상을 가지기때문에,\n모델 운영상의 리소스나 레이턴시 관점에서 아직은 조금 부담이 될수도 있기때문에 768차원의 비교적 가벼운 임베딩으로\n좋은 성능을 보이는 BERT, RoBERTa와 같은 모델들이 여전히 많이 사용된다.\n학습 목표 # 우선 이번에 내가 Fine-Tuning을 통해 얻고자하는것을 간단히 정리하면 아래와 같다.\n\u0026lt;현재 상태\u0026gt;\n서비스중인 챗봇에서는 Function Call과 RAG를 통해 답변의 품질을 향상시키고 있음. 사용자의 질문에 대한 적절한 API를 찾는 Function Call Retrieval과정에서 임베딩 모델을 통해 유사도 기반 Top10 API를 LLM에게 추천함 LLM은 추천된 모델의 Description을 통해 사용할 API를 선택하여, 호출하게 됨. \u0026lt;해결 과제\u0026gt;\nTop10 API의 Description을 LLM에게 input하는 과정에서 많은 token비용이 소모됨 Top5의 API만 높은 정확도로 추천하여, LLM input시 발생하는 Token비용을 절감하자! 등록된 API가 많아지게 되면서, 도메인 언어의 미묘한 차이를 임베딩 모델이 구분하지 못하고, 유사 API들을 혼동하는 경우가 많아짐 도메인 텍스트에 대한 Fine-Tuning을 통해, 모델의 도메인 텍스트에 대한 임베딩 성능을 높이자! 학습데이터 # 유저들이 입력한 챗봇 질문 데이터(회계/세법 관련 질문) - Unlabeled data API 설명데이터 (API의 기능을 설명한 Description) - Unlabeled data\n\u0026gt; 1+2 도합 약 42만건의 문장 데이터 API 설명 + 사용자 질문 Pair set - Labeled data \u0026gt; 3번 : 약 4,600건의 pair set 데이터 # 학습 모델 # 나는 이번에 한국어 Pre-trained Embedding Model을 특정 도메인(회계/세법)을 더 잘표현할 수 있는 임베딩 모델로\nFine-Tuning을 진행하려한다.\n학습할 모델은 RoBERTa기반의 pre-trained model을 선택했다.\n해당 모델은 아래를 참고하기 바란다.\nhttps://huggingface.co/BM-K/KoSimCSE-roberta-multitask\n[BM-K/KoSimCSE-roberta-multitask · Hugging Face\nhttps://github.com/BM-K/Sentence-Embedding-is-all-you-need Korean-Sentence-Embedding 🍭 Korean sentence embedding repository. You can download the pre-trained models and inference right away, also it provides environments where individuals can train mode\nhuggingface.co](https://huggingface.co/BM-K/KoSimCSE-roberta-multitask)\n위 모델을 선택한 이유는, 최근 좋은 한국어 pre-trained embedding model을 찾기위해 서칭하던 중,\n허깅페이스에 공개된 몇가지 임베딩 모델과 OpenAI의 임베딩 모델(text-embedding-3-large API)들을 비교 테스트한적이 있었는데, 회계/세법 도메인에서는 비교적 위 모델이 가장 좋은 성능을 보였다.\n모델의 baseline성능이 좋을 경우, Fine-Tuning결과도 좀 더 기대해봄직하기때문에 위 모델을 선택하게 되었다.\n# 학습 방식 # 1) SimCSE # 학습방식으로는 contrastive learning 방법중 하나인 SimCSE 방식으로 학습을 진행하기로 했다.\n이번 학습에 사용될 데이터로 회계/세법데이터를 마련하긴했으나, pair간 관계정보가 labeling된 데이터가 소수(4,600건)있었고,\nunlabeled data(단순 sentences)가 420,000건 정도 있었다.\n우선 SimCSE 방식은 supervised learning방식과 unsupervised learning 방식을 모두 지원하고 있고,\n간편한 Unsupervised 구현 방식에도 불구하고, 매우 높은 학습 성능을 보여주는 학습 방법이다.\nSimCSE방식에서의 Unsupervise learning 방법을 간단하게 설명하자면,\n먼저, 동일한 문장을 쌍으로 만들어 이를 각각 drop-out이 적용된 다른 layer에 input해서 약간의 차이를 가지는 vector를 구한다.\n이렇게 만들어낸 pair를 positive pair로 간주하고, 해당 문장과 동일 batch내의 다른 문장과는 negative pair 관계로 간주하여\n데이터를 구성한다. 그리고, Loss로는 contrastive learning 방식에 일반적으로 활용되는 InfoNCE Loss를 활용하여,\nPositive pair간의 거리를 가깝게하고, Negative Pairs의 거리를 멀게하도록 학습하여\nVector 공간내에서 문장들의 위치를 최적화 하는 학습방식이다.\n(SimCSE 방식을 아주 심플하게 설명했는데, SimCSE 방식의 특징에 대해서, 다음 기회에 포스팅 해보도록 하겠다.)\n💡TIP (중요)\nSimCSE 방식으로 학습할때, 데이터셋을 구성하는 팁을 한가지 설명하자면,\n**한개의 Batch내의 텍스트들은 서로 독립된 의미를 가지도록 최대한 shuffle을 신경써서 해야한다는 점이다!**batch내의 문장은 각각 본인문장 x 2를 positive pair,\n본인문장과 다른문장을 묶어 negative pair로 구성하게 되는데,\n만약 배치내에서 유사문장이 섞여있을 경우, (본인문장, 유사문장) 으로 묶인 데이터는 실제로는 의미적으로 유사한\ncontext를 가지고 있지만, negative pair로 분류되어 vector공간에서 서로 멀어지도록 학습되기때문에,\n이런 데이터가 많을 경우 학습 성능이 매우 떨어지게 된다.\n따라서, batch내의 문장들은 가급적 서로 의미적으로 관계성이 낮은 문장들로 묶어줄 필요가 있다.\nSimCSE는 이러한 방식으로 unsupervised learning만으로도 높은 학습 성능을 보이는 방법으로 최근에도 많이 활용되는\n학습 방식이기때문에, Fine-Tuning 학습 방식으로 선택하게 되었다.\nRef. https://arxiv.org/abs/2104.08821\n2) LoRA # 우선, 예전에 임베딩 모델의 Full Fine-Tuning을 진행한적이 있었다.\n마찬가지로 도메인 성향이 강한 텍스트를 통해 Supervised learning방식의 contrastive learning을 진행했는데,\n도메인 텍스트를 제외한 일반적 텍스트의 임베딩 성능은 크게 떨어졌다.\nFull Fine-Tuning을 제대로 할 경우, 모델의 모든 weight parameter를 업데이트 할 수 있기때문에 잘 학습하면,\n최적의 임베딩 성능을 뽑아낼 수 있지만, 그것은 질 좋은 데이터를 균형있게 잘 구성했을때 최적화가 가능하다는 점을 명심해야한다.\nFull Fine-tuning의 경우, 기존에 학습했던 데이터와 같은 일반적인 학습 데이터와 새로 학습할 데이터를 균형있게 잘 넣어주어야하고,\n많은 데이터를 학습에 사용해야, 좋은 성능을 기대할 수 있다.\n현재 내가 학습하고자하는 데이터셋은 강한 도메인 성향을 가진 데이터만으로 구성되어있고,\n일반적 텍스트 데이터가 없기 떄문에, PEFT 방식의 학습을 진행하기로 결정했고,\n그중에서도 높은 성능을 보이는 LoRA 방식으로 Fine-Tuning을 진행하려한다.\n💡TIP만약, 다양한 학습데이터를 통해 주기적 또는 다양한 데이터셋을 통해 여러번의 Fine-Tuning과정이 필요하다고 생각된다면,\nLoRA의 사용을 조금 고민해 봐야한다.\nLoRA는 adapter에서 설정한 rank에 따라, 차원을 compression - decompression 하는 과정을 거치게되는데,\n논문에서 권장하는 rank는 4~8차원으로, 낮은 차원 공간을 가진 weight matrix에서 다양한 학습내용을 담아야한다.\n이 때문에, 최초 fine-tuning에서는 높은 학습 성능을 보이지만, 반복된 fine-tuning에서는 앞전의 학습내용\u001d을 망각 하는 문제점이 발생한다.\n이러한 문제 때문에, 만약 여러번의 fine-tuning을 진행해야하는 task에서는 유사한 아키텍쳐를 가지면서, LoRA의 망각 현상을 개선한 \u0026lsquo;MoRA\u0026rsquo; 학습방식을 고민해보시길 권장드린다.\n만약, LoRA의 개념을 다시 한번 확인해보고자 한다면, 이전에 LoRA 포스팅한 내용을 담은, 아래 링크를 참고해보시기 바란다.\nhttps://familia-89.tistory.com/102\n[[PEFT] LoRA(Low-Rank Adaptation of Large Language Models) Fine-Tuning 학습 방식 뜯어보기! (내부 학습 코드 해석)\n오늘은 최근 Fine-Tuning 시 가장 많이 사용하는 매서드중 하나인 LoRA에 대해서 정리해보도록 하자. 먼저 LoRA는 PEFT로 대표되는 Fine-Tuning 학습 방법이고, 최근 LoRA에서 파생된, QLoRA, MoRA 등 매서드들\nfamilia-89.tistory.com](https://familia-89.tistory.com/102)\n학습 Process # 1) LoRA Architecture\n자, 이제 학습 방법론은 SimCSE와 LoRA방식을 활용하는것으로 정리했는데, 한가지 문제가 있다.\n보통 LoRA를 통해 Fine-Tuning을 진행할때,\nPEFT라이브러리에서 제공하는 LoraConfig라는 클래스를 이용해서 간단하게 LoRA를 설정한 후, 학습을 진행할 수 있다.\nfrom transformers import AutoModelForSeq2SeqLM from peft import get_peft_config, get_peft_model, LoraConfig, TaskType model_name_or_path = \u0026#34;bigscience/mt0-large\u0026#34; tokenizer_name_or_path = \u0026#34;bigscience/mt0-large\u0026#34; peft_config = LoraConfig( task_type=TaskType.SEQ_2_SEQ_LM, inference_mode=False, r=8, lora_alpha=32, lora_dropout=0.1 ) model = AutoModelForSeq2SeqLM.from_pretrained(model_name_or_path) model = get_peft_model(model, peft_config) model.print_trainable_parameters() \u0026#34;trainable params: 2359296 || all params: 1231940608 || trainable%: 0.19151053100118282\u0026#34; #ref.https://github.com/huggingface/peft/tree/main 위 코드는 PEFT Github에서 제공하는 LoraConfig 를 통한 간단한 학습 예제코드이다.\n학습하고자하는 모델의 성격에 따라, task_type 인자에 적절한 옵션을 선택할 수 있다.\n다만, task_type인자에서 임베딩 모델에 대한 contrastive learning 옵션을 현재는 제공하고 있지 않은것으로 보인다.\n따라서, 임베딩 모델 학습 시, 아직은 PEFT 라이브러리를 사용할 수 없기 때문에,\nLoRA 학습 아키텍처를 직접 구성해서 사용해야한다.\n아래는 LoRA 학습 아키텍처를 구현한 코드 내용이다.\nimport torch from torch import nn from transformers import RobertaModel, RobertaTokenizer from transformers.models.roberta.modeling_roberta import RobertaSelfAttention class LoRA(nn.Module): def __init__(self, model, r=8, alpha=16, dropout_rate=0.1): super(LoRA, self).__init__() self.model = model self.r = r self.alpha = alpha self.scaling = alpha / r self.dropout_rate = dropout_rate for param in self.model.parameters(): param.requires_grad = False for name, module in self.model.named_modules(): if isinstance(module, RobertaSelfAttention): self._inject_lora(module) def _inject_lora(self, module): for proj in [\u0026#34;query\u0026#34;, \u0026#34;key\u0026#34;, \u0026#34;value\u0026#34;]: weight = getattr(module, proj).weight bias = getattr(module, proj).bias out_features, in_features = weight.shape lora_A = nn.Linear(in_features, self.r, bias=False) lora_B = nn.Linear(self.r, out_features, bias=False) nn.init.normal_(lora_A.weight, std=0.02) nn.init.zeros_(lora_B.weight) lora_proj = LoRAProjection(weight, bias, lora_A, lora_B, self.scaling, dropout_rate=self.dropout_rate) setattr(module, proj, lora_proj) def forward(self, *args, **kwargs): return self.model(*args, **kwargs) class LoRAProjection(nn.Module): def __init__(self, weight, bias, lora_A, lora_B, scaling, dropout_rate=0.0): super(LoRAProjection, self).__init__() self.weight = weight self.bias = bias self.lora_A = lora_A self.lora_B = lora_B self.scaling = scaling if dropout_rate \u0026gt; 0.0: self.dropout = nn.Dropout(p=dropout_rate) else: self.dropout = None def forward(self, x): lora_out = self.lora_A(x) if self.dropout is not None: lora_out = self.dropout(lora_out) lora_out = self.lora_B(lora_out) return nn.functional.linear(x, self.weight, self.bias) + self.scaling * lora_out 먼저, LoRA의 기본 원리를 간단히 되짚어보자면,\n기존의 pre-trained model weight parameter는 freezing한 후,\nlast_hidden_state를 가져와 별도로 구성된 lora adapter에 input한다.\nlora adapter의 output을 기존 모델의 output값에 더해, 값을 보정한다.\n정도로 요약할 수 있다.\nLoRA의 동작원리를 염두에두고, 위 코드를 부분별로 확인해보자.\n1) Parameter Define\ndef __init__(self, model, r=8, alpha=16, dropout_rate=0.1): super(LoRA, self).__init__() self.model = model self.r = r self.alpha = alpha self.scaling = alpha / r self.dropout_rate = dropout_rate LoRA클래스에서는, lora학습에 사용할 몇가지 옵션들을 초기화하고있다.\n먼저, loara adapter를 적용할 backbone model을 인자로 받게된다.\nr인자는, adapter 크기를 결정할 rank옵션에 해당하는 값을 의미한다.\n그리고, 반영정도(scale)를 조절할 alpha 옵션등을 인자로 받아 LoRA학습 프로세스로 반영할 수 있게 정의하고\n마지막으로 dropout 비율을 정의하게된다.\n2) weight freezing \u0026amp; LoRA projection 주입하기\nfor param in self.model.parameters(): param.requires_grad = False for name, module in self.model.named_modules(): if isinstance(module, RobertaSelfAttention): self._inject_lora(module) fine-tuning의 타겟이 될 Backbone model의 weight parameter가\n학습과정에서 업데이트되지 않도록 Freezing처리 한다.\n그리고, 모델의 모듈을 순회하면서, self-attention 모듈에 해당할 경우, LoRA projection을 주입한다.\n3) Adapter layer 정의\ndef _inject_lora(self, module): for proj in [\u0026#34;query\u0026#34;, \u0026#34;key\u0026#34;, \u0026#34;value\u0026#34;]: weight = getattr(module, proj).weight bias = getattr(module, proj).bias out_features, in_features = weight.shape lora_A = nn.Linear(in_features, self.r, bias=False) lora_B = nn.Linear(self.r, out_features, bias=False) nn.init.normal_(lora_A.weight, std=0.02) nn.init.zeros_(lora_B.weight) lora_proj = LoRAProjection(weight, bias, lora_A, lora_B, self.scaling, dropout_rate=self.dropout_rate) setattr(module, proj, lora_proj) _inject_lora 매서드는 LoRAProjection을 주입하기 위한 매서드이다.\n먼저, 각 프로젝션(key, query, value)의 weight를 가져온 후 정규분포로 초기화를 진행한다.\n그리고, Adapter A, B에 해당하는 layer를 설정된 rank에 맞게 정의한다.\n마지막으로 weight, bias, adapter 등 정의된 값을 통해 LoRAProjection 클래스를 instantiation한다.\n4) LoRAProjection\nclass LoRAProjection(nn.Module): def __init__(self, weight, bias, lora_A, lora_B, scaling, dropout_rate=0.0): super(LoRAProjection, self).__init__() self.weight = weight self.bias = bias self.lora_A = lora_A self.lora_B = lora_B self.scaling = scaling if dropout_rate \u0026gt; 0.0: self.dropout = nn.Dropout(p=dropout_rate) else: self.dropout = None def forward(self, x): lora_out = self.lora_A(x) if self.dropout is not None: lora_out = self.dropout(lora_out) lora_out = self.lora_B(lora_out) return nn.functional.linear(x, self.weight, self.bias) + self.scaling * lora_out LoRAProjection에서는 인자로 받은 weigt, bias, adapter, dropout, scaling을 통해서, LoRA Adapter를\n학습하는 부분을 담고있는데, LoRA A와 B adapter를 차례로 거친 output을\n기존 weight, bias가 계산된 결과에 더하게 된다.\n덧셈 방식으로 가중치를 반영하는 이유는, 기존 모델의 결과값에 \u0026lsquo;미세조정\u0026rsquo;하는 효과를 얻기 위해서 이다.\n기본적으로 행렬곱이 발생할 경우, 벡터공간에서 결과값은 완전하게 다른 방향 또는 다른 위치의 정보를 갖게되는데,\n덧셈방식을 적용할 경우, 약간의 위치 조정만 반영 되면서, 미세조정의 효과를 얻을 수 있게된다.\n2) SimCSE Dataset\n자, 다음은 SimCSE 학습을 위한 데이터셋 및 데이터로더를 구현하는 부분에 대해 정리해보도록 하자.\nSimCSE 학습 데이터 구성방식 : https://arxiv.org/pdf/2104.08821v4\nSimCSE 논문에 표현된 위 그림을 통해 SimCSE의 학습데이터 구성방식을 이해할 수 있다.\n먼저, Unsupervised learning 쪽을 먼저 확인해보자.\nSimCSE는 대표적인 contrastive learning 방식으로, 학습데이터로 구성된 pair간의 비교관계를 통해\n의미관계를 학습하는 방식이다.\n이때, pair로 구성된 쌍의 각 원소들은 비슷한 의미를 가지거나(positive pair), 반대의 의미를 가짐(negative pair)으로써,\n문장간의 멀고 가까운 관계 정보를 통해 모델이 context를 학습하게 된다.\n이때, SimCSE는 이러한 관계정보 없이, 단순 문장의 나열된 데이터셋만으로 supervised learning 방식\n이상의 효과를 거두고 있는 연구이다.\n만약 아래와 같은 텍스트 데이터가 있다고 가정해보자.\n[\n\u0026lsquo;오늘 날씨 어때?\u0026rsquo;,\n\u0026lsquo;지금 몇 시야?\u0026rsquo;,\n\u0026lsquo;근처 카페 추천해 줘.\u0026rsquo;,\n\u0026lsquo;오늘 일정 확인해 줘.\u0026rsquo;\n\u0026hellip;\n]\n위는 단순 문장의 나열일 뿐, pair 쌍 형태의 학습 정보가 존재하지 않는다.\nSimCSE 방식은 여기에, 각 문장별로 동일한 문장을 이어 붙여 쌍을 생성한다.\n예를들어, (\u0026lsquo;오늘 날씨 어때?\u0026rsquo;, \u0026lsquo;오늘 날씨 어때?\u0026rsquo;) 와 같이 같은 문장을 pair로 묶는다.\n동일한 문장은 같은 Vector를 나타내는데, 어떻게 학습이되는지 궁금할 수 있는데,\ndrop-out을 통해 차이를 만들어 학습하게 된다.\n[Positive Pair]\npair의 문장은 동일한 문장이지만, 서로 다른 layer내에서 drop-out을 적용받게되면서,\n비슷하지만 약간의 차이를 가진 vector로 변환된다.\n그리고, 이 pair쌍은 동일한 문장이기때문에, 학습과정에서 거리가 최소화 되도록 학습해야한다.\n그래서,가까운것은 더 가깝게, 멀리있는것은 더 멀리 위치하도록 하는 특성을 가진 InfoNCE Loss를 통해,\n두개 문장의 벡터상 거리를 좁히는 방식으로 학습을 진행하게 된다.\n[Negative Pair]\n이때, 마찬가지로 위 나열된 문장 데이터만으로 Negative pair 또한 생성해낼 수 있는데,\n첫번째, 문장인 \u0026ldquo;오늘 날씨 어때?\u0026rdquo; 라는 문장과 나머지 문장들을 하나씩 돌아가며 쌍을 지어,\n간단한게 negative pair를 생성할 수 있다.\n다만, 데이터셋 생성과정에서 위 데이터들을 일일이 만들어줄 필요는 없다.\n그 이유는 뒤의 training 과정에서 설명하도록 하겠다.\n3) Trainer \u0026amp; \u0026amp; InfoNCE Loss\nfor cnt, batch in enumerate(iterator): optimizer.zero_grad() input_ids, attention_mask = batch[\u0026#39;input_ids\u0026#39;].to(device), batch[\u0026#39;attention_mask\u0026#39;].to(device) outputs1 = model(input_ids, attention_mask=attention_mask, return_dict=True) hidden_states1 = outputs1.last_hidden_state embeddings_1 = hidden_states1[:, 0, :].to(device) outputs2 = model(input_ids, attention_mask=attention_mask, return_dict=True) hidden_states2 = outputs2.last_hidden_state embeddings_2 = hidden_states2[:, 0, :].to(device) loss = info_nce_loss(embeddings_1, embeddings_2, loss_temperature, device) if loss\u0026lt;best_loss: best_loss = loss early_stop_num=0 early_stop_num+=1 if early_stop_num\u0026gt;=early_stop_limit: break if logging: wandb.log({\u0026#39;Nce_loss[Train]\u0026#39;: loss.item()}) loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) optimizer.step() 위는 trainer.py 파일의 학습코드 일부를 발췌 했다.\n위 코드를 보면 기존 pre_trained model에 batch데이터를 두번 input하여 \bdrop-out을 각각 적용받은 embedding\u001d 추출한다.\n그리고, 이 두개의 임베딩을 loss에 넣게된다.\n그리고, 실제로 SimCSE 방식을 적용하는 내용은 Loss 함수 내부에 담기게 되는데, 아래 코드를 확인해보자.\ndef info_nce_loss(embeddings_1, embeddings_2, temperature, device): sim_matrix = F.cosine_similarity(embeddings_1.unsqueeze(1), embeddings_2.unsqueeze(0), dim=-1) / temperature batch_size = sim_matrix.size(0) labels = torch.arange(batch_size).to(device) loss_fn = torch.nn.CrossEntropyLoss() loss = loss_fn(sim_matrix, labels) return loss 위의 info_nce_loss의 인자로 받게되는 embedding_1, embedding_2의 경우 각각 batch_size의 길이를 가진 vector로 구성되어있다.\n그리고, 두 임베딩 배열간의 유사도를 구하게 될 경우, batch_size x batch_size형태의 유사도 행렬이 산출된다.\n이때, 유사도 행렬의 대각선에 해당하는 유사도들은 positive_pair간의 유사도를 나타내고 나머지 유사도들은 negative pair간의 유사도를 의미하게 된다.\n구분 A문장 \bB문장 C문장 A문장 유사도 AA (Positive Pair) 유사도 BA (Negative Pair) 유사도 CA (Negative Pair) B문장 유사도 AB (Negative Pair) 유사도 BB (Positive Pair) 유사도 CB (Negative Pair) C문장 유사도 AC (Negative Pair) 유사도 BC (Negative Pair) 유사도 CC (Positive Pair) 즉, 유사도 행렬을 통해 positive pair간의 유사도와 negative pair유사도를 모두 생성할 수 있고,\n이를 통해 info nce loss를 적용할 수 있게 된다.\n자, 지금까지 학습코드의 주요 부분에 대해서 코드레벨로 프로세스를 확인해보았다.\n다만, 위 코드대로 학습을 진행할때, 몇가지 이슈사항이 있었는데,\n혹시 추후 해당 프로세스를 참고하여 Fine-Tuning을 진행할때, 꼭 참고해서 학습해주시길 바란다.\n1) batch_size는 작게 설정해야한다.\n처음에 적절한 batch_size를 고민했을때, SimCSE논문을 참고했었다.\nhttps://arxiv.org/abs/2104.08821\n논문에서는 RoBERTa모델의 경우, 512 batch_size로 테스트를 진행했고, 긍정적인 결과를 얻었다고 기재되어있다.\n하지만, 위 학습 프로세스를 따를때 주의할 점은 batch_size를 매우 작게 설정해야한다는 것이다.\n왜냐하면, 논문에서는 RoBERTa 아키텍쳐의 모든 weight parameter를 활용한 full fine-tuning을 진행했다.\n따라서, batch_size가 크게 설정되더라도, 배치내 많은 수의 데이터에 대한 context를 학습할 충분한 공간이 있었다.\n다만, 우리는 논문과는 다르게, 기존 학습 parameter들은 freezing하고, LoRA adapter 부분만을 통해 학습을 진행하기때문에\n큰 batch_size내의 다양한 context를 반영할 공간이 상대적으로 부족하다.\n이때문에 batch_size를 작게 가져가는것이 학습에 유리하다.(실제로 batch_size를 낮췄을때, 성능도 올라갔다.)\n내가 테스트한 데이터셋에서는 batch_size=8 정도에서 가장 좋은 성능을 보였다.\n2) Learning Rate는 너무 크지 않게 설정해야한다.\nLoRA Adapter만이 학습 대상이 되기 때문에, Learning Rate를 우선 너무 크지 않게 잡는게 중요하다.\n이번에 내가 학습한 데이터셋을 기준으로는 1e-4 ~ 3e-4에서 가장 좋은 성능을 보였다.\n반복적인 Fine-Tuning 학습이 예상된다면, LoRA를 사용하지않는것을 권장드린다. 위에서도 잠깐 언급했지만, LoRA는 Adapter만을 학습 공간으로 활용하기때문에,\n다양하고 풍부한 데이터를 반복적으로 학습하기에는 공간이 부족하다는 단점이 있다.\n때문에, 반복적 Fine-Tuning이 이뤄질 경우, 초기 학습한 내용에 대한 망각 현상이 발생한다.\n이를 보완하기 위해, Adapter의 Rank를 늘리는 방법이 있기는 하지만,\n이를 통해 해결할 경우, LoRA 고유의 장점을 잃어버릴 수 있기때문에\n적절한 값을 찾는것이 중요해진다.\n최근 LoRA의 이러한 단점을 보완하기 위해, Adapter의 input과 output이 멱등한 구조를 가지고,\nadapter 앞뒤로 compression, decompression 과정을 추가하여,\n반복학습에도 망각현상을 줄인 \u0026lsquo;MoRA\u0026rsquo;라는 아키텍처가 있으니,\n이것을 참고하시길 권장드린다.\n# https://www.mokosmart.com/ko/lora-technology/\n[What is LoRa Technology and How it Works - An In-depth Guide\nLoRa technology is LPWAN protocol connecting battery-powered things to the internet with long-range connectivity and low-power communication.\nwww.mokosmart.com](https://www.mokosmart.com/ko/lora-technology/)\nhttps://huggingface.co/BM-K/KoSimCSE-roberta-multitask\n[BM-K/KoSimCSE-roberta-multitask · Hugging Face\nhttps://github.com/BM-K/Sentence-Embedding-is-all-you-need Korean-Sentence-Embedding 🍭 Korean sentence embedding repository. You can download the pre-trained models and inference right away, also it provides environments where individuals can train mode\nhuggingface.co](https://huggingface.co/BM-K/KoSimCSE-roberta-multitask)\nhttps://arxiv.org/abs/2104.08821\n[SimCSE: Simple Contrastive Learning of Sentence Embeddings\nThis paper presents SimCSE, a simple contrastive learning framework that greatly advances state-of-the-art sentence embeddings. We first describe an unsupervised approach, which takes an input sentence and predicts itself in a contrastive objective, with o\narxiv.org](https://arxiv.org/abs/2104.08821)\n","date":"2024.10.15","externalUrl":null,"permalink":"/posts/101/","section":"글","summary":"","title":"[Fine-Tuning] SimCSE + LoRA를 활용해서 임베딩 모델을 Fine-Tuning해보자!","type":"posts"},{"content":"","date":"2026.10.15","externalUrl":null,"permalink":"/categories/ai-engineering/","section":"Categories","summary":"","title":"AI Engineering","type":"categories"},{"content":"","date":"2026.10.15","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","date":"2026.10.15","externalUrl":null,"permalink":"/tags/colbert/","section":"Tags","summary":"","title":"Colbert","type":"tags"},{"content":"","date":"2026.10.15","externalUrl":null,"permalink":"/tags/embedding/","section":"Tags","summary":"","title":"Embedding","type":"tags"},{"content":"고객의 현장에서 업무 구조와 환경, 그리고 잠재된 불편함을 분석하고, 사용자 경험을 개선할 수 있는 도메인 특화 AI 서비스 및 에이전트를 개발합니다. 기술보다 문제의 본질을 탐구하는 것을 중요하게 생각합니다.\n프로젝트 보기 →개발 타임라인 →\n","date":"2026.10.15","externalUrl":null,"permalink":"/","section":"Kim Won Tae","summary":"","title":"Kim Won Tae","type":"page"},{"content":"","date":"2026.10.15","externalUrl":null,"permalink":"/tags/kure/","section":"Tags","summary":"","title":"Kure","type":"tags"},{"content":" 1. 검토 배경 # 2026년 8월 17일, nlpai-lab 에서 KURE-v2 를 공개했습니다.\n현재 모 손해보험사 에이전트에서는 인덱싱 전용 임베딩 모델로 KURE-v1 을 보험 도메인 데이터로 fine-tuning 한 모델(지난 글)을 쓰고 있습니다. 그런데 새로 나온 KURE-v2 가 MTEB(kor, v2) 리더보드 1위를 기록해서, 차기 프로젝트에 적용할 만한지 도입 타당성을 검토해봤습니다.\n2. KURE-v2 구조 이해 # 2.1 Backbone # 백본은 skt/A.X-Encoder-base 이고, ModernBERT 계열 154M 파라미터 모델입니다. BAAI/bge-m3 기반(568M)인 KURE-v1 의 약 1/4 크기입니다. 백본 인코더 output 에 dense pooling(토큰 × 768dim 을 mean pooling 으로 1 × 768dim 으로)을 붙이면 단일벡터 모델이 되고, 토큰별 projection(토큰 × 768dim 에 128dim layer 를 투사해서 토큰 × 128dim 으로)을 붙이면 멀티벡터 모델이 됩니다. KURE-v2 는 후자를 선택했습니다. 2.2 핵심 차이 # 구분 KURE-v1 (dense) KURE-v2 (multi-vector) Shape 1,024차원 벡터 1개 토큰마다 128차원 벡터 생성 방식 토큰 벡터를 mean pooling 으로 압축 토큰 벡터(encoder output)를 linear projection 으로 768 → 128 축소 후 그대로 보존 유사도 계산 벡터 간 내적 1회 MaxSim (질의 토큰 × 문서 토큰 전수 대조) 질의 길이 가변 (최대 8,192) 64 토큰 고정 (패딩 / 절삭) 인덱스 HNSW PLAID / BinaryIVF (전용) 2.3 MaxSim (late interaction) # 질의 토큰 각각에 대해 문서 토큰 전체와 유사도를 계산하고, 행별 최댓값만 뽑아서 합산합니다. 질의와 문서를 미리 섞지 않고 검색 시점에 늦게(late) 맞대 본다는 의미에서 late interaction 이라고 부릅니다.\n위 그림에서 굵은 테두리 3개가 서로 다른 열에 있다는 점이 핵심입니다. 질의 단어들이 문서의 각기 다른 위치에서 매칭되는 구조라, 단일벡터의 \u0026ldquo;평균 압축\u0026rdquo; 문제가 사라지고 질의 단어들이 문서 여기저기에 흩어져 있을 때 강합니다.\n3. 성능 # 지표 KURE-v1 KURE-v2 MTEB(kor, v2) nDCG@10 0.7616 (dense 중 8위) 0.8160 (전체 1위) MLDR-ko (장문 검색) 0.4521 0.6914 Param 568M 154M License MIT Apache-2.0 파라미터는 1/4 인데 MTEB(kor, v2) 는 +0.0544, 장문 검색(MLDR-ko)은 +0.2393 을 기록했습니다. 특히 긴 문서에서 격차가 크게 벌어지는데, dense 벡터는 평균으로 압축하는 과정에서 장문의 정보를 잃기 때문입니다.\n4. Index 크기 # 일반적으로 멀티벡터는 토큰마다 벡터를 저장하니 원칙적으로는 용량이 큽니다. 다만 KURE-v2 는 두 가지 압축을 적용해서 오히려 dense 벡터보다 작아지도록 만들었습니다.\n4.1 토큰 풀링 (hierarchical token pooling) # 텍스트를 잘라서 토큰을 줄이는 게 아니라, 백본이 벡터를 다 만든 뒤에 이미 생성된 벡터 중 유사한 것끼리 묶는 방식입니다. 유사도 판정은 코사인, 병합은 Ward linkage 계층 클러스터링, 클러스터 대표는 mean pooling 으로 만듭니다. (원본 벡터는 버립니다) 묶는 개수는 pool_factor 로 조절합니다. 거리 임계값이 아니라 상한 클러스터 수를 고정해두는 방식입니다. (최대 토큰수 / pool_factor + 1 개) 문서에만 적용하고 질의에는 적용하지 않습니다. 학습이 필요 없고, 인덱싱 시점에 적용하는 후처리입니다. (Ref. arXiv 2409.14683, Clavié · Chaffin · Adams, 2024-09) 4.2 비대칭 이진 양자화 (asymmetric binary quantization) # 문서 토큰 벡터의 각 차원을 1비트만 남깁니다. (bf16 16비트 → 1비트) 질의는 bf16 을 그대로 둡니다. 이게 \u0026ldquo;비대칭\u0026rdquo; 의 의미인데, 문서는 수억 개가 저장되지만 질의는 요청당 1개라 양자화해도 이득이 거의 없어서, 한쪽만 양자화해서 오차를 줄이는 방식입니다. 인덱스는 9.4배 줄어들고, nDCG@10 손실은 1.05 (×100 스케일) 입니다. 차원 수 128 은 그대로이고, 줄어드는 건 토큰 개수와 숫자 하나당 비트 수뿐입니다. 5. 도입 검토 결과 # 긴 context 에 강점이 있는 모델이라, 인덱싱 용도로 fine-tuning 해서 쓰고 있는 KURE-v1 의 대체 후보로 놓고 접근했습니다. 적용 방식은 두 가지를 고민했습니다. KURE-v1 이 맡고 있는 인덱싱 모델 자리를 그대로 바꾸는 방법(경로 A)과, 기존 RAG 검색 성능을 높이는 Rerank 모델로만 쓰는 방법(경로 B)입니다.\n다만, 아래 리스크 때문에 KURE-v2 도입 검토는 철회하고, 아직은 KURE-v1 을 유지하기로 했습니다.\nAzure AI Search 를 쓸 수 없습니다. Multi-Vector 방식의 모델을 쓰려면 MaxSim 기반 검색이 필요한데, Azure AI Search 는 이를 지원하지 않습니다. 결국 Vespa, Weaviate, Milvus, Qdrant 처럼 MaxSim 을 지원하는 DB 를 VM 안에 self-hosted 로 구축해야 하고, 그러면 SLA, 백업, 복구, 버전 업그레이드, HA 구성 같은 인프라 부담을 새로 떠안게 됩니다. 검색 로직을 새로 개발해야 합니다. VectorDB 와 인덱싱 형태가 달라지니 기존 검색 로직을 그대로 쓸 수 없습니다. latency 가 문서 길이에 따라 들쭉날쭉합니다. 기존 dense vector 는 문서가 길든 짧든 1,024dim 벡터 간 유사도 계산 한 번이면 끝나지만, multi-vector 는 토큰 개수 × 128차원 벡터 간 연산이라 검색된 문서가 클수록 MaxSim 연산량과 latency 가 같이 커집니다. 검색 속도의 일관성이 떨어지고, p99 쪽으로 갈수록 부담이 매우 커집니다. 한국어 형태소 분석기를 못 씁니다. 고품질의 Azure AI Search 한국어 형태소 분석기를 활용하지 못하게 되어, lexical search 성능이 떨어질 수 있다는 우려도 있었습니다. 6. 결론 # Multi-Vector 기반 임베딩 모델은 토큰 단위 vector 구조로 장문의 맥락 소실 문제를 크게 개선했습니다. 다만 장문 임베딩 성능이 특별히 요구되는 상황이 아니라면, 위에서 정리한 인프라 부담과 연산 부하, latency 문제 때문에 프로덕션에서 기존 dense 임베딩 모델을 교체할 동기는 아직 부족하다고 판단했습니다.\n","date":"2026.10.15","externalUrl":null,"permalink":"/posts/kure-v2-review/","section":"글","summary":"","title":"KURE-v2 도입 검토 후기 → Multi-Vector 임베딩은 왜 아직 못 쓰나","type":"posts"},{"content":"","date":"2026.10.15","externalUrl":null,"permalink":"/tags/maxsim/","section":"Tags","summary":"","title":"Maxsim","type":"tags"},{"content":"","date":"2026.10.15","externalUrl":null,"permalink":"/tags/multi-vector/","section":"Tags","summary":"","title":"Multi-Vector","type":"tags"},{"content":"","date":"2026.10.15","externalUrl":null,"permalink":"/tags/rag/","section":"Tags","summary":"","title":"Rag","type":"tags"},{"content":"","date":"2026.10.15","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"},{"content":"","date":"2026.10.15","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"2026.10.15","externalUrl":null,"permalink":"/posts/","section":"글","summary":"","title":"글","type":"posts"},{"content":"","date":"2026.10.15","externalUrl":null,"permalink":"/series/%EB%B3%B4%ED%97%98-%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%9E%84%EB%B2%A0%EB%94%A9/","section":"Series","summary":"","title":"보험 도메인 임베딩","type":"series"},{"content":"","date":"2026.09.28","externalUrl":null,"permalink":"/tags/fine-tuning/","section":"Tags","summary":"","title":"Fine-Tuning","type":"tags"},{"content":"","date":"2026.09.28","externalUrl":null,"permalink":"/tags/koelectra/","section":"Tags","summary":"","title":"Koelectra","type":"tags"},{"content":"","date":"2026.09.28","externalUrl":null,"permalink":"/tags/ner/","section":"Tags","summary":"","title":"Ner","type":"tags"},{"content":"","date":"2026.09.28","externalUrl":null,"permalink":"/tags/onnx/","section":"Tags","summary":"","title":"Onnx","type":"tags"},{"content":"","date":"2026.09.28","externalUrl":null,"permalink":"/tags/pii/","section":"Tags","summary":"","title":"Pii","type":"tags"},{"content":"","date":"2026.09.28","externalUrl":null,"permalink":"/tags/quantization/","section":"Tags","summary":"","title":"Quantization","type":"tags"},{"content":"지난 글에서 에이전트 안에서 Tool·Agent 를 찾는 검색용 e5 모델을 만들었다면, 이번에는 긴 약관 문서를 VectorDB 에 적재할 때 쓰는 인덱싱 전용 임베딩 모델을 만든 기록입니다. 결과 모델은 Hugging Face 에 wt-kure-insurance-v1 으로 공개해 두었습니다.\n1. 학습 목적 # \u0026ldquo;VectorDB 적재용 long-text 인덱싱 특화 한국어 보험 임베딩 모델 개발\u0026rdquo;\n이전 실험에서 OpenAI text-embedding-3-large 의 한국어 성능이 떨어지는 것을 확인했습니다. 앞서 만든 wt-e5-small-ko-insurance-v1 은 max context length 가 512 라서, 다양한 길이 특히 긴 context 를 임베딩해야 하는 인덱싱 작업에는 맞지 않습니다. 그리고 보험 도메인에서 사내 자산으로 쓸 수 있는 온프렘 임베딩 모델의 수요가 앞으로 더 생길 것으로 봤습니다. 정리하면, OpenAI 모델보다 한국어 임베딩 성능은 높으면서 long context 임베딩은 동등하게 지원하는 온프렘 모델이 필요했습니다.\n2. 모델 선택 → KURE-v1 # 아래 조건으로 후보를 골랐습니다.\n기본 한국어 임베딩 성능이 SoTA 급일 것 인덱싱 목적에 맞게 long context 임베딩을 지원할 것 (OpenAI 와 같은 8,192 토큰 이상) 여러 도메인에서 고른 성능을 보이고, 오랜 기간 많은 사용 사례로 검증된 모델일 것 GPU 가 없는 프로젝트에서도 쓸 수 있게 CPU 로도 구동 가능한 크기일 것 이 조건들에 가장 잘 맞는 nlpai-lab/KURE-v1 (bge-m3 backbone · 568M · 1,024 dim · max_seq 8,192)을 최종 선택했습니다.\n(e5 를 쓰지 않은 이유는 단순합니다. e5 는 context 가 512 토큰이라 페이지 단위로 청킹해서 인덱싱하기엔 부족했습니다.)\n3. 데이터 구성 # 3.1 데이터 출처와 가공 파이프라인 # 원본은 모 손해보험사가 운영하는 보험상품의 공식 문서 PDF 437건입니다. 상품당 약관, 사업방법서, 상품요약서가 평균 2.7종씩 있고, 총 42,626 페이지 분량입니다.\n원본 PDF → 페이지 텍스트 : PyMuPDF 로 페이지 단위 추출 L1 청크 풀 : (product_id × doc_type) 단위로 이어 붙인 뒤 tiktoken cl100k_base 기준 4가지 길이(256 / 1,024 / 4,096 / 7,500 토큰)로 분할, stratified sampling 으로 15,500 청크 L2 합성 query : 청크마다 GPT-5.4 로 3가지 스타일(keyword / natural / specific) query 생성 → 46,500 query L3 hard negatives : base KURE 임베딩으로 cosine top-50 을 뽑고, NV-Retriever 3-tier 규칙(cross-product / same-product / extra)으로 query 당 8개 mining → 372,000 negative L4 split : product_id 단위로 train / eval / test = 80 / 15 / 5 분리 (row-level random 분리는 leakage 가 생겨서 쓰지 않았습니다) 일반 도메인 망각 여부를 보기 위한 외부 데이터로는 KLUE 벤치마크의 한국어 STS 519쌍(뉴스·위키 등, 보험과 무관)을 따로 두었습니다.\n3.2 4-resolution 청크 통계 # resolution 학습 sample 수 평균 토큰 약관 사업방법서 상품요약서 256 5,000 236 3,989 602 409 1,024 5,000 787 3,975 611 414 4,096 3,000 3,168 2,276 420 304 7,500 2,500 6,165 1,892 355 253 합계 15,500 12,132 1,988 1,380 3.3 Query 합성 (청크당 3 variants) # 실제 사용자의 검색 패턴은 키워드 검색, 자연어 질문, 긴 시나리오 질문까지 다양합니다. 한 청크를 어떤 식으로 물어봐도 찾을 수 있도록, 청크 하나에 서로 다른 스타일의 query 를 3개씩 만들었습니다.\nvariant 토큰 범위 스타일 학습 효과 keyword 5 ~ 20 키워드 나열형 짧은 검색 query robustness natural 30 ~ 80 자연어 한 문장 질문 일반 사용자 query 분포 specific 50 ~ 150 다중 조건 / 시나리오 명시 복합 정보 검색 예를 들어 사업방법서의 256 토큰짜리 청크 하나(\u0026ldquo;보험료는 일시납을 원칙으로 하되, 필요한 경우 분할 영수할 수 있다 …\u0026quot;)에 대해서는 아래 3개가 만들어졌습니다.\nkeyword : 보험료 분할 영수 일시납 natural : 이 상품의 보험료는 분할해서 납입할 수 있나요? specific : 풍수해·지진재해보험법 제7조에 따른 보험료 지원이 있을 때, 국가/지자체 지원분과 주민 부담분을 따로 분할 영수받을 수 있는지 그리고 일시납 원칙과 어떻게 충돌하지 않는지 알려주세요 각 query 는 학습 시 (query, positive = 원본 청크, hard_negatives[K=4]) triplet 으로 묶여 들어갑니다.\n(참고로 eval set 은 보통 best checkpoint 선정에 쓰이기 때문에 최종 평가에 섞으면 bias 가 생길 수 있어서 지양해야 합니다. 다만 이번 학습은 epoch 수가 적어 그 위험이 낮았고, 별도의 도메인 평가 데이터셋이 없어서 eval set 도 최종 평가에 포함했습니다.)\n4. Loss Function → MNRL # contrastive 학습의 표준인 MultipleNegativesRankingLoss 를 그대로 썼습니다. query 가 자기 positive 에는 가깝고, 다른 query 의 positive 들과 미리 mining 해둔 hard negative 들과는 멀어지도록 학습합니다.\nL = - (1/B) Σ_i log( exp(s(q_i, p_i+) / τ) / [ Σ_j exp(s(q_i, p_j) / τ) + Σ_k exp(s(q_i, p_k-) / τ) ] ) s(q, p) = cosine similarity (L2-normalized embeddings) τ = 0.05 (temperature) B = micro-batch size (per resolution) 분모 = in-batch negatives (B-1) + mined hard negatives (K=4) 5. 학습 파라미터 # 항목 값 설명 base model nlpai-lab/KURE-v1 bge-m3 backbone · 568M. LoRA adapter 만 학습, backbone 은 frozen LoRA rank / alpha / dropout 16 / 32 / 0.05 adapter 약 7.1M params (전체의 1.24%) · 12MB target modules query, key, value, dense 12개 transformer layer 전체 learning rate 1e-4 base 가중치 변동을 적게 해서 일반화 성능을 지키는 쪽으로 weight decay / warmup 0.01 / 0.1 epochs 3 loss 0.73 → 0.21 per-resolution batch 256: 32, 1024: 16, 4096: 8, 7500: 4 긴 seq 일수록 VRAM 부담이 커서 batch 를 줄임 effective batch 64 micro-batch 가 64 에 도달할 때까지 grad accumulate hard negatives 4 8개 mining 후 상위 4개만 사용 (+ in-batch negatives) precision bf16 + gradient checkpointing 학습 장비 · 시간 H100 80GB × 1 · 약 15시간 21분 peak VRAM 18.6GB 5.1 Curriculum (epoch 별 resolution 비중) # epoch 마다 resolution 별 sampling 비중을 바꿔서, 짧은 데이터로 먼저 워밍업한 뒤 뒤로 갈수록 긴 데이터에 집중하도록 했습니다.\nepoch 256 1,024 4,096 7,500 1 50% 30% 15% 5% 2 20% 25% 25% 30% 3 5% 15% 30% 50% 6. 학습 중 Loss 추이 # 초기 loss 0.729 → 최종 0.206 으로 수렴했습니다. epoch 경계마다 튀는 구간은 curriculum 으로 resolution 비중이 바뀌는 지점입니다.\n각 epoch 이 끝날 때 eval split 일부(resolution 당 corpus 300)로 빠르게 retrieval 을 재서, mean Recall@5 가 가장 높은 epoch 의 adapter 를 best 로 저장했습니다.\nepoch mean Recall@5 per-resolution Recall@5 1 98.00% 256 = 100.0 · 1024 = 100.0 · 4096 = 97.7 · 7500 = 94.3 2 98.25% 256 = 100.0 · 1024 = 100.0 · 4096 = 96.7 · 7500 = 96.3 3 98.17% 256 = 99.7 · 1024 = 100.0 · 4096 = 97.0 · 7500 = 96.0 7. 평가 # 7.1 평가 데이터와 비교 모델 # In-domain (eval split, 5,397 query) : 학습에 참여한 상품의 미사용 query Out-of-domain (test split, 759 query) : 학습에 아예 참여하지 않은 상품 (product_id 단위 분리) KorSTS (519 pairs) : 일반 한국어 문장 유사도 Spearman 상관. catastrophic forgetting 확인용 모델 dim max_seq 역할 ft (FT KURE, 이번 모델) 1,024 8,192 제안 base (KURE-v1) 1,024 8,192 한국어 SoTA, 학습 전 upper-bound e5 (wt-e5-small-ko-insurance-v1) 384 512 이전 글의 도메인 FT 모델 openai (text-embedding-3-large) 3,072 8,191 상용 SoTA (e5 는 max context 가 512 라서 1,024 이상의 청크는 잘려서 정보가 손실됩니다. 공정한 비교를 위해 e5 는 256 resolution 에서만 평가했고, 긴 문장 비교는 base KURE 와 OpenAI 로만 진행했습니다.)\n7.2 In-domain 결과 # Recall@1\nresolution ft base e5 openai ft vs base ft vs openai 256 96.32% 93.23% 93.75% 89.73% +3.09pp +6.60pp 1,024 94.77% 88.46% - 76.51% +6.31pp +18.26pp 4,096 82.06% 65.16% - 37.02% +16.90pp +45.04pp 7,500 77.74% 55.59% - 18.22% +22.15pp +59.52pp Recall@3\nresolution ft base e5 openai ft vs base ft vs openai 256 99.36% 98.60% 98.89% 97.37% +0.76pp +1.98pp 1,024 99.77% 97.95% - 92.32% +1.82pp +7.45pp 4,096 96.03% 84.61% - 55.15% +11.43pp +40.89pp 7,500 93.66% 78.32% - 32.53% +15.34pp +61.13pp 7.3 Out-of-domain 결과 (학습에 없던 상품) # resolution R@1 ft R@1 base R@1 openai R@3 ft R@3 base R@3 openai 256 100.00% 100.00% 99.58% 100.00% 100.00% 100.00% 1,024 95.24% 93.94% 88.31% 100.00% 100.00% 98.70% 4,096 94.12% 86.93% 51.63% 100.00% 99.35% 77.12% 7,500 83.70% 74.07% 31.85% 97.04% 95.56% 54.07% 7.4 긴 문서일수록 격차가 벌어지는지 # 짧은 문서(256)와 긴 문서(7,500)에서 각각 비교 모델과의 차이를 구해서 빼 봤습니다. 값이 양수면 긴 문서에서 격차가 더 크다는 뜻입니다.\n비교 (eval split) 256 Δ 7,500 Δ long − short R@1 ft vs base +3.09 +22.15 +19.05 R@1 ft vs openai +6.60 +59.52 +52.92 R@3 ft vs base +0.76 +15.34 +14.58 R@3 ft vs openai +1.98 +61.13 +59.15 test split 에서도 같은 방향(R@1 기준 base 대비 +9.63, openai 대비 +51.44)이었습니다. 짧은 문서에서는 다들 비슷하게 잘 찾지만, 문서가 길어질수록 OpenAI 모델은 급격히 무너지고 fine-tuning 한 모델은 버텨 줍니다.\n7.5 KorSTS (일반화 forgetting 확인) # model Spearman ft 대비 ft 0.8766 base 0.8774 -0.07pp e5 0.7996 +7.70pp openai 0.8234 +5.32pp 보험 데이터로 학습한 뒤에도 일반 한국어 문장 유사도는 base 와 거의 같게(-0.07pp) 유지됐습니다.\n7.6 Latency # batch = 8, 3회 평균, query 1건당 ms 기준입니다. (CPU 는 Apple M-series 로컬, GPU 는 학습 프로파일 기반 추정치입니다.)\n모델 · 환경 seq 128 seq 512 seq 1,024 seq 4,096 seq 7,500 ft · GPU (H100, bf16) 3.0 6.5 13.0 52.0 124.0 ft · CPU 107.8 273.6 517.0 2,488.4 6,805.3 base · CPU 83.1 226.3 426.0 2,309.3 5,867.8 e5 · CPU 21.2 31.7 - - - openai · API (private endpoint, 네트워크 포함) 115.0 127.0 123.1 174.3 156.3 adapter 가 붙는 만큼 base 보다 조금 느려지고, 7,500 토큰 문서를 CPU 로 임베딩하면 건당 6.8초 정도 걸립니다.\n8. 정리 # 도메인 성능 : eval split 기준 base 대비 R@1 평균 +12.11pp, R@3 평균 +7.34pp. 학습에 없던 상품(test split)에서도 R@1 평균 +4.53pp 올랐습니다. 일반화 : KorSTS -0.07pp 로 사실상 망각 없이 유지됐습니다. 비교 모델 : 256 resolution 에서 e5 와는 1승 3무, OpenAI 와는 전체 16개 비교에서 14승 2무였습니다. (R@1 + R@3 평균으로 OpenAI 대비 +25.61pp) 긴 문서 : 7,500 토큰 문서의 R@1 이 base 대비 +22.15pp, OpenAI 대비 +59.52pp 로, 문서가 길수록 격차가 커졌습니다. e5(검색용)와 KURE(인덱싱용)는 지금도 같은 에이전트 안에서 역할을 나눠 쓰고 있습니다. 짧은 query 로 Tool·Agent 를 고르는 건 가볍고 빠른 e5 가, 긴 약관을 쪼개서 VectorDB 에 적재하는 건 KURE 가 맡는 구조입니다.\n그리고 이후에 KURE-v2 가 공개되면서 이 모델을 교체할지 한 번 더 검토했는데, 그 내용은 다음 글에 정리했습니다.\nHugging Face : https://huggingface.co/1T/wt-kure-insurance-v1 ","date":"2026.05.20","externalUrl":null,"permalink":"/posts/insurance-embedding-kure/","section":"글","summary":"","title":"Indexing 임베딩 모델 도메인 최적화 → 긴 약관을 위한 KURE-v1 LoRA Fine-Tuning","type":"posts"},{"content":"","date":"2026.05.20","externalUrl":null,"permalink":"/tags/long-context/","section":"Tags","summary":"","title":"Long-Context","type":"tags"},{"content":"","date":"2026.05.20","externalUrl":null,"permalink":"/tags/lora/","section":"Tags","summary":"","title":"Lora","type":"tags"},{"content":"","date":"2026.05.11","externalUrl":null,"permalink":"/tags/e5/","section":"Tags","summary":"","title":"E5","type":"tags"},{"content":"","date":"2026.05.11","externalUrl":null,"permalink":"/tags/mnrl/","section":"Tags","summary":"","title":"Mnrl","type":"tags"},{"content":"","date":"2026.05.11","externalUrl":null,"permalink":"/tags/retrieval/","section":"Tags","summary":"","title":"Retrieval","type":"tags"},{"content":"모 손해보험사의 보험 설계 에이전트를 개발하면서, 에이전트 내부에서 Tool 과 Sub-Agent 를 찾아오는 검색용 임베딩 모델을 보험 도메인에 맞춰 fine-tuning 했습니다. 결과 모델은 Hugging Face 에 wt-e5-small-ko-insurance-v1 으로 공개해 두었습니다.\n(같은 시리즈의 다음 글에서는 긴 약관 문서를 VectorDB 에 적재하기 위한 인덱싱 전용 임베딩 모델을 다룹니다.)\n1. 개발 배경 # 1) 한국어 성능 측면 # 과거 프로젝트 경험상, OpenAI 의 임베딩 모델(이전 버전인 text-embedding-3-large 등)은 다국어 학습 모델이지만 보험·금융처럼 한국어 특수 도메인에서는 한국어로 pre/fine-tuning 된 오픈소스 모델 대비 retrieval 정확도가 많이 떨어졌던 경험이 있었습니다.\n이에, 한국어로 선학습된 모델에 보험 도메인 데이터로 fine-tuning 을 진행하면, 지금 개발 중인 에이전트의 Tool·Agent Retrieval 성능을 끌어올릴 수 있을 것으로 판단했습니다.\n2) 프로덕션 운용 측면 # 온프렘 후보로 검토한 dragonkue/multilingual-e5-small-ko-v2 의 경우, 118M params · 384 dim 의 작은 모델임에도 기본 한국어 embedding 및 retrieval 성능이 좋습니다.\n물론, dimension 을 1,024 까지 올려서 후보군을 찾으면 BGE-M3, KURE-v1 같은 SoTA 모델이 있긴 합니다. 다만, 과거 프로덕션 환경에서 1,024 dimension 을 넘어가는 모델을 CPU 로 돌렸을 때 cpu 바운드로 인한 부하와 latency 이슈를 겪었던 적이 있어서, 에이전트단에서 CPU 로 구동할 모델은 768 dimension 미만으로 범위를 좁혀서 찾았습니다.\n그리고 실제로 e5-small-ko-v2 를 에이전트에 붙여 보니, 기존 text-embedding-3-large 대비 response latency 가 30배 이상 빠르면서도 Retrieval 성능은 OpenAI 모델을 상회하는 것을 확인해서, 모델 교체의 근거로 삼을 수 있었습니다.\n3) 규제 대응 → Region 이슈 사전 차단 # 이 부분은 프로젝트를 같이 하던 동료가 짚어준 내용입니다.\n데이터 국외 반출 제한 관점에서, OpenAI text-embedding-3-large 는 Korea Central + Standard SKU 로 배포할 수 있습니다. 다만 이 조건은 물리 데이터는 한국에 있지만 국외에서 access 가 가능한 구조라, 혁신금융서비스 심사 과정에서 문제가 될 소지가 있었습니다.\n이에, 외부 API 의존을 줄이고 온프렘 모델로 전환하면 region / data residency 이슈를 근본적으로 차단할 수 있다고 봤습니다.\n2. 학습 모델 선택 → e5-small-ko-v2 # 한국어 embedding SoTA 인 KURE-v1 과 경량 모델 중 가장 성능이 좋은 e5-small-ko-v2 를 후보로 두고, OpenAI text-embedding-3-large 와 3자 비교를 진행했습니다. 비교는 고객사가 준 \u0026ldquo;질문별 호출 대상 Agent·Tool 매핑\u0026rdquo; 데이터로 만든 테스트셋 104건 기준입니다.\n성능만 보면 KURE-v1 이 가장 좋았습니다. 그런데 아래처럼 e5-small-ko-v2 가 압도적으로 가볍고 빠르면서도 운영 가능한 수준의 Retrieval Accuracy 를 보여서, 최종 fine-tuning 대상으로 골랐습니다.\nmodel Dimension Disk RAM (MB) Latency e5-small-ko-v2 (base) 384 470MB 213 7.4 ms e5-small-ko-v2 + LoRA 384 + 2.6MB + 5 13 ms KURE-v1 1,024 2.3GB 1,493 49.4 ms 3. 목표 # \u0026ldquo;보험 도메인 특화 임베딩 모델 개발\u0026rdquo;\n특정 고객사 데이터뿐 아니라 보험 도메인 전반의 embedding 성능 강화 실제 과제의 Agent·Tool Retrieval 성능 최적화 e5-small-ko-v2 의 일반화 성능 유지 (overfitting 지양) 4. 학습 설계 # 1) 학습 데이터 구축 # 데이터는 크게 실 데이터, LLM 생성 데이터, 오픈소스 데이터 세 갈래로 구성했습니다.\n구분 데이터 역할 건수 실 데이터 실제 Tool description positive corpus 15 실 데이터 실제 Sub-Agent description positive corpus 5 LLM 생성 (gpt-5.4-mini) 실 Tool 합성 query 실 데이터 기반 유사 데이터 증강 15 × 80 = 1,200 LLM 생성 실 Sub-Agent 합성 query 실 데이터 기반 유사 데이터 증강 5 × 80 = 400 LLM 생성 가상 보험 Tool desc + query 보험 도메인 일반 증강 5 desc, 1,200 query LLM 생성 가상 기타 Tool desc + query 공공·금융 등 타 도메인 일반화 10 desc, 620 query 오픈소스 klue/mrc 일반 한국어 retrieval 3K ~ 10K 오픈소스 klue/sts (label ≥ 3.5) 일반 의미 paraphrase 2K ~ 7.5K (처음에는 약관 PDF 원문 42,626 페이지도 넣었는데, 오히려 Retrieval 성능이 떨어져서 뺐습니다. 이유는 7장 시행착오에 적어두었습니다.)\n2) Loss Function → MNRL # MultipleNegativesRankingLoss(MNRL)는 같은 batch 안의 다른 sample 들의 positive 를 negative pool 로 자동으로 쓰는, 임베딩 학습에서 대표적으로 쓰이는 contrastive loss 입니다. 정답 쌍은 가깝게, 나머지 쌍은 멀어지도록 학습합니다.\n예를 들어 batch = 16 이면, anchor_i 기준으로 같은 배치 안의 나머지 15개 sample 의 positive_j 가 전부 anchor_i 의 negative 가 됩니다. 그래서 negative 쌍을 따로 만들 필요가 없습니다.\nLoss 단점 Triplet loss (단일 anchor-pos-neg) negative 를 1개만 써서 학습 신호가 약함 BCE / pointwise ranking 학습이 어려움 MNRL (채택) batch 의 모든 sample 이 자동으로 negative → 학습 pool 이 넓어짐 3) Model Architecture → LoRA # 한국어 일반화 성능은 최대한 유지하면서 보험 도메인 성능만 올리기 위해, 기존 가중치 대부분은 freezing 하고 일부 레이어(adapter)만 학습하는 LoRA 방식을 썼습니다.\n기존 base node 의 output 에 adapter node 의 output 을 \u0026ldquo;더하는\u0026rdquo; 구조라, vector space 안의 weight 와 bias 가 통째로 바뀌는 문제를 막아줍니다. 그래서 도메인 데이터에 대한 쪽집게 학습에 쓰기 좋습니다. 실제 최종 모델도 전체 117M 파라미터 중 405K(0.44%)만 학습했고, 나머지 99.6%는 freezing 된 채로 두었습니다. (학습 후 저장된 adapter 크기는 약 1.5MB)\nLoRA 동작 방식은 예전에 정리해둔 LoRA 학습 방식 뜯어보기 글을 참고하면 됩니다.\n항목 값 의미 epochs 4 r (rank) 8 adapter 행렬 차원. 클수록 capacity ↑, 망각도 ↑ alpha 16 scaling factor (= 2r 관례). 실효 학습률 = lr × 2 dropout 0.05 adapter 출력 5% drop (regularization) target_modules query, key, value, dense 4종 module 에 LoRA 부착 layers_to_transform null (전체 12 layer) 모든 layer 에 부착 bias None bias 는 학습하지 않음 task_type FEATURE_EXTRACTION classification head 없이 embedding 만 5. Train # H100 80GB GPU 1장으로 학습했습니다.\n학습 데이터는 MNRL 정규 포맷(query / positive / negatives)으로 만들었고, query 와 passage 앞에는 e5 계열 관례대로 prefix 를 붙였습니다.\n{ \u0026#34;query\u0026#34;: \u0026#34;query: 해약하면 얼마 돌려받아?\u0026#34;, \u0026#34;positive\u0026#34;: \u0026#34;passage: 해약환급금조회 (getCancellationReturnAmount)\\n[기능] 보험계약 해약 시 시점별 환급금을 조회한다.\\n[동작 방식] 계약번호 + 해약 예정일 입력, 환급금/공시이율/사업비 차감 후 금액 계산.\\n입력: ctrno (필수), expectDate (선택)\\n출력: returnAmount, surrenderRatio, deductedFee\u0026#34;, \u0026#34;negatives\u0026#34;: [ \u0026#34;passage: 보험금 청구 진행 조회 (getClaimProgress)\\n[기능] 청구 접수된 건의 진행 단계와 담당자 정보를 조회한다 ...\u0026#34;, \u0026#34;passage: 보험료 납입 내역 조회 (getPremiumPaymentHistory)\\n[기능] 특정 계약의 최근 12개월 납입 내역을 조회한다 ...\u0026#34;, \u0026#34;passage: 계약 상태 조회 (getPolicyStatus)\\n[기능] 계약의 현재 상태(유효/실효/해지)와 다음 납입 예정일을 반환한다 ...\u0026#34; ] } 6. Result # 1) 평가 기준 # 기준 설명 도메인 평가셋 고객사 시트에서 발췌한 실제 query 104건 NDCG@10 top-10 안에서 정답이 위에 있을수록 높은 점수 R@1 / R@3 Tool 을 1개 / 3개 뽑았을 때 정답 포함 여부 MRR@3 정답 순위 역수의 평균 (1위 1.0, 2위 0.5, 3위 0.33, 밖이면 0) Holdout-1056 gpt-5.4-mini 로 만든 대규모 holdout 1,056 query 의 종합 ranking 품질 Extra holdout 학습에 참여하지 않은 25개 sector 신규 tool(실손·카드·통신·세금 등) 750 query. 일반화 능력 측정 General Tool Calling 환율·영화·항공편 등 tool calling 테스트용 공개 데이터셋 (heegyu/glaive-function-calling-v2-ko) 2) 평가 결과 # 기준 base e5-small-ko-v2 LoRA (wt-e5-small-ko-insurance-v1) OpenAI 3-large 도메인 NDCG@10 0.630 0.773 (+14.3pt) 0.634 도메인 R@1 0.385 0.596 (+21.1pt) 0.337 도메인 R@3 0.562 0.716 (+15.4pt) 0.635 도메인 MRR@3 0.492 0.670 (+17.8pt) 0.463 Holdout-1056 NDCG@10 0.8886 0.9053 (+1.7pt) 0.8766 Extra holdout NDCG@10 0.8616 0.9025 (+4.1pt) 0.8985 General Tool Calling NDCG@10 0.5175 0.5283 (+1.1pt) 0.5193 General Tool Calling R@10 0.7312 0.7502 (+2.0pt) 0.7417 Dimension 384 384 3,072 추론 latency p50 7 ms 13 ms 네트워크 의존 의존성 로컬 로컬 OpenAI API + private endpoint 도메인 평가셋에서는 base 대비 NDCG@10 이 14.3pt 올랐고, OpenAI 3-large(0.634)보다도 높게 나왔습니다. 그리고 학습에 쓰지 않은 신규 tool(Extra holdout)과 공개 tool calling 데이터셋에서도 점수가 떨어지지 않고 소폭 올라서, 일반화 성능도 유지된 것으로 봤습니다.\n7. 시행착오 # 한 번에 된 건 아니고, 아래처럼 꽤 여러 번 돌았습니다.\nversion Issue Try Result v1 도메인 align 학습 시도 고객사 홈페이지에서 모은 약관, 상품요약서로 학습 일반화·도메인 스코어 모두 대폭 하락 (-11.1pt) v2 일반화 성능 저하 1트 : klue/mrc + klue/sts mix-in 일반화 드리프트 약간 회복 (holdout +3.2pt) 2트 : KL Distillation (base 출력을 정답 referent 로 써서 LoRA 가 너무 멀어지지 않게) 일반화는 덜 튀었지만 여전히 소폭 하락, 도메인은 거의 그대로 3트 : target_modules 축소 (Q/K/V/dense → Q + V) 일반화는 보존, 도메인 점수 하락 4트 : layers_to_transform 으로 상위 N 개 layer 만 부착 (top-4 / 6 / 8) top-6 Q+V 가 holdout 최고치. 단 도메인 0.657 로 capacity 부족 5트 : 다시 Full(12 layer × Q+K+V+dense = 48 module) + epoch 2 → 4 holdout, 도메인 모두 최고 성능 v1 의 경우, 약관과 상품요약서에 담긴 개념적인 워딩이 실제 Agent·Tool Retrieval 의 문장 패턴이나 맥락과는 성격이 맞지 않아서 오히려 성능이 떨어진 것으로 보입니다. 약관 데이터는 tool description 검색이 아니라 정보 검색 목적의 임베딩에서 효과가 있을 데이터라고 판단했고, 이 판단이 다음 글의 인덱싱 전용 모델로 이어졌습니다.\n그리고 4트에서 일반화를 지키려고 학습 범위를 줄일수록 도메인 점수가 내려가는 걸 보고, trade-off 구간에 들어선 게 아닐까 의심했었습니다. 결과적으로는 범위를 줄이는 게 답이 아니었고, 도메인 데이터 대비 학습 강도가 부족했던 쪽이었습니다.\n8. 마치며 # OpenAI text-embedding-3-large 는 다국어 모델이긴 하나, 한국어 특수 도메인에서는 한국어로 pre/fine-tuning 된 모델의 base 성능이 이미 꽤 큰 차이로 앞섰습니다. 데이터가 많지 않아도(실 description 20건 + 증강) 경량 임베딩 모델의 도메인 성능을 충분히 끌어올릴 수 있었습니다. 프로젝트에서 온프렘 모델로 OpenAI 임베딩을 대체할 수 있다는 걸 확인했고, 다른 도메인에서도 자체 임베딩 모델을 운용할 수 있는 레퍼런스를 하나 확보했습니다. Hugging Face : https://huggingface.co/1T/wt-e5-small-ko-insurance-v1 ","date":"2026.05.11","externalUrl":null,"permalink":"/posts/insurance-embedding-e5/","section":"글","summary":"","title":"Retrieval 임베딩 모델 도메인 최적화 → 보험 도메인 e5-small-ko-v2 LoRA Fine-Tuning","type":"posts"},{"content":"","date":"2025.03.31","externalUrl":null,"permalink":"/tags/deadlock/","section":"Tags","summary":"","title":"Deadlock","type":"tags"},{"content":"","date":"2025.03.31","externalUrl":null,"permalink":"/categories/dev-notes/","section":"Categories","summary":"","title":"Dev Notes","type":"categories"},{"content":"","date":"2025.03.31","externalUrl":null,"permalink":"/tags/gil/","section":"Tags","summary":"","title":"Gil","type":"tags"},{"content":"","date":"2025.03.31","externalUrl":null,"permalink":"/tags/global-interpreter-lock/","section":"Tags","summary":"","title":"Global Interpreter Lock","type":"tags"},{"content":"","date":"2025.03.31","externalUrl":null,"permalink":"/tags/i/o/","section":"Tags","summary":"","title":"I/O","type":"tags"},{"content":"파이썬은 느리다.\n우리가 파이썬을 처음 접했을때부터 많이 들어오던 말이다.\n물론, 최근에는 파이썬 내부적으로도 많은 개선이 이뤄지고 있고, C++로 동작하는 다양한 라이브러리들이 많아지면서\n속도에 대해 체감할 수 있는 수준의 발전이 있었다.\n하지만, 여전히 저수준의 언어들 C, C++, Rust 에 비해서 느린것은 사실이다.\n그렇다면, 파이썬은 왜 느리고, 어떻게 개선할 수 있을까?\n물론, 다양한 원인과, 다양한 개선방안이 있겠지만,\n그 중 대표적으로 I/O 바운드 로직에서 Python에서는 GIL의 존재로 인해,\n병렬처리가 실질적으로 어려워 지연시간이 발생하는 문제를 그 중 하나로 꼽을 수 있다.\n이때, 해결방법으로 보통 떠올리는것이 Threading을 활용한 해결일것이다.\n다만, 모든 경우에 threading으로 해결할 수 있는것은 아니며,\nLatency문제가 발생할 경우, Threading으로 해결할 수 있을지, 없을지 정확히 판단하는것이 우선일 것이다.\n오늘은 Python에서 Thread를 효율적으로 활용하기 위해,\nGIL(Global Interpreter Lock), Reference Count, GC(Garbage Collect)등의 개념을\n이해하고, Thread를 어떤 경우에 잘 활용할 수 있는지에 대해 살펴보도록 하자.\nThread란? # https://velog.io/@jaewan/CSPythonThread1\n먼저 Thread란 프로세스가 가진 실행 흐름의 여러 갈래중 하나를 의미한다.\n쉽게 말해, 한 프로그램(프로세스) 안에서 동시에 여러 작업(실행 흐름)을 진행하기 위해 사용하는 기술을 말한다.\n예를들어, 주방에서 여러 요리를 준비하는 상황에 빗대어 설명하면,\n* 프로세스 : 식당 주방 전체를 하나의 ‘프로세스’라고 가정할 수 있다.\n* 스레드 : 주방에서 일하는 여러 요리사 각각을 ‘스레드’로 비유할 수 있다.\n즉, **하나의 주방(프로세스)**에서 여러 **요리사(스레드)**가 동시에 다른 작업을 처리하니, 전체적인 조리 시간이 단축된다.\n이때, 요리사(스레드)들은 동일한 재료나 도구(메모리 자원 등)를 공유해서 사용해야 하므로 충돌(race condition)을 피하고 위한\n규칙(동기화, mutex, lock)이 필요하다.\n아래는 스레드를 구현한 간단한 코드이다.\nimport threading import time # 스레드로 실행할 함수 def worker(name): for i in range(3): print(f\u0026#34;{name} 스레드, i={i}\u0026#34;) time.sleep(1) # 메인 스레드 if __name__ == \u0026#34;__main__\u0026#34;: # 두 개의 스레드를 생성 t1 = threading.Thread(target=worker, args=(\u0026#34;A\u0026#34;,)) t2 = threading.Thread(target=worker, args=(\u0026#34;B\u0026#34;,)) # 스레드 시작 t1.start() t2.start() # 메인 스레드는 t1, t2가 끝날 때까지 기다림 t1.join() t2.join() print(\u0026#34;모든 스레드 종료\u0026#34;) worker() 함수가 하는일은 단순히 메시지를 1초 간격을 3번 출력하는것인데, t1스레드와 t2스레드를 생성한 후 실행하면 worker(“A”)와 woker(“B”)가 동시에(또는 시분할) 실행되는것 처럼 동작한다.\n이와 같이 Thread는 첫번째 작업의 완료를 기다리지 않고, 동시에 여러 작업을 수행할 수 있어 CPU 자원을 좀 더 효율적으로 사용할 수 있다는 장점을 가지고 있다.\n(다만, 위와 같은 Python에서는 GIL로 인해, 실제로 동시 실행(병렬동작)은 아니다.)\n자 그럼, Thread의 특징을 몇가지 살펴보자.\n1) 메모리 공유 # 같은 프로세스 내부의 스레드들은 프로세스의 메모리 공간(힙, 전역변수 등)을 공유한다. 이는 스레드 간 데이터를 주고 받기 쉽다는 장점은 있지만, 서로 공유 자원을 동시에 수정하면 문제(동기화 이슈 등)가 발생할 수 있다 2) 독립된 실행 흐름 # 각 스레드는 자신만의 프로그램 카운터, 레이스터 세트, 스택을 가진다. 덕분에 동시에(또는 시분할 방식으로)다른 코드를 실행할 수 있다. 3) 가벼운 생성/소멸 비용 # 프로세스 하나를 새로 띄우는 것보다 스레드 하나를 생성하는 것이 일반적으로 자원 소모가 적고 빠르다. 여러 스레드를 활용하면, 프로세스를 여러 개 띄우는 것보다 효율적인 경우가 많다. # 스레드가 사용되는 예시는 어떤것들이 있을까? # 1) 동시처리(Concurrency) \u0026amp; 병렬 처리(Parallelism) # 동시 처리 : 예를 들어, 서버가 동시에 여러 클라이언트 요청을 받을 때, 각 요청을 따로 처리하도록 스레드를 사용할 수 있다. 병렬 처리 : 멀티코어 CPU 환경에서 여러 스레드가 실제로 같은 시점에 병렬로 수행될 수 있다. 이를 통해 성능을 높일 수 있다. * 예시 : 웹 서버\nMain 스레드가 새로운 클라이언트 요청이 들어오면, 스레드를 새로 생성하거나 스레드 풀에서 하나를 가져와서 요청을 처리하게 한다. 동시에 여러 요청이 들어와도, 각각이 독립된 스레드로 처리되므로, 대기 시간이 단축되고 빠른 응답이 가능하다. 2) 백 그라운드 작업 # “서비스에서 중요한 작업이지만, 즉각적인 응답을 줄 필요가 없는 경우”\n영상 인코딩 : 사용자가 동영상을 업로드하면, 웹 서버는 “업로드 완료”플래그를 바로 전달하고, 실제 동영상 인코딩 작업은 백그래운드 스레드에서 별도로 실행 데이터 집계/분석 : 시간마다 통계를 내거나 로그를 수집/분석하는 작업은 별도의 스레드로 처리할 수 있다. # 3) 비동기 I/O 처리 # 네트워크 I/O나 디스크 I/O등 입출력 작업은 대기시간이 길수있는데, 이때, 하나의 스레드가 I/O 대기 때문엔 가만히 멈춰있으면 전체 시스템 효율이 떨어진다. 이때, 여러 스레드를 운용하면, 다른 스레드는 CPU 작업을 계속 이어서 수행할 수 있어 시스템 자원을 효율적으로 활용할 수 있다. Thread의 장점은 병렬계산. 하지만, Python에의 Thread는 실제로 병렬적으로 수행되지 않는다? # 파이썬에서는 GIL(Global Interpreter Lock)이 적용되는데, GIL은 한마디로 “동일한 시점에 둘 이상의 스레드가 Python 바이트코드를 동시에 실행하지 못하도록(동시에 2개 연산이 CPU에서 진행되지 않도록) Lock하는 장치를 의미한다.\n결국 GIL로 인해, CPU연산이 많은 코드를 멀티 스레드로 작성하더라도 “실제로는 하나의 스레드만 CPU 연산을 수행하게 되기때문에 100%를 활용한 병렬 계산이 되지않는다.\n\u0026ldquo;응? 그렇다면, Thread의 병렬계산 자체가 불가능하다는 의미가 아닌가? 그렇다면 multi Thread의 의미가 있는가?\u0026rdquo;\n라고 생각할 수 있다.\n조금 더 설명해보겠다.\nGIL은 주로 파이썬 내부 구조와 메모리 관리(reference count)매커니즘 때문에 사용된다.\n여기서 Reference Count란, 예를들어 어떤 객체(정수, 리스트, 클래스 인스턴스 등)를 참조하는 ‘변수’가 생기면 reference count 가\n1증가하고, 변수가 사라지면(del) 1이 감소한다.\n만약 reference count가 0이 되면, 해당 객체를 메모리에서 해제한다.\n이와 같이 해당 방식을 통해 파이썬에서는 자동으로 메모리를 관리를 수행한다.\n다만, 이러한 메모리 관리 방식에서도 순환참조(Circular Reference)의 경우에는 해결이 어려운데,\n예를들어 A가 B를 참조하고, B가 A를 참조할 경우, reference count는 0이 될 수 없다.\n이때에는 직접 garbage collect(gc.collect()) 매서드 호출을 통해 참조 관계를 초기화할 수 있다.\n다시 돌아와서, 병렬 스레딩을 사용하면 위와 같은 Reference Count방식을 꼬이게 만들 수 있다.\n만약, 객체에 대해 여러 스레드에서 동시 참조를 진행하게 될 경우, reference count가 잘못된 순서로 늘어나거나, 적어지는(데이터손상) 현상이 발생할 수 있는것이다.\n이러한 문제을 예방하기 위해 \u0026ldquo;GIL이 동일 시점에 오직 하나의 스레드만이 파이썬 바이트코드를 실행하도록 제한하여,\n문제를 예방하는 역할을 한다.\u0026rdquo;\n결국, 이러한 역할 및 특성 때문에, Python에서 실제로 병렬 스레딩 동작은 GIL로써 제한되는것이다.\n(다만, C의 확장 라이브러리(Numpy, Pytorch, SciPy 등)를 활용할 경우 내부적으로 GIL을 해제한 채로 작업하기때문에 병렬계산이 가능하다)\n다만, 그럼에도 병렬 연산이 제한되는 Python에서도 멀티 스레딩이 가지는 효과는 존재한다.\n효과에 대해 이해하기 위해 먼저 아래에서 I/O바운드 작업, CPU바운드 작업에 대해서 이해해보자.\nI/O 바운드 작업 - CPU 바운드 작업 # I/O바운드 작업 : 프로그램의 성능이 “입출력(I/O) 대기시간”에 의해 주로 결정되는 작업을 의미한다. CPU 바운드 작업 : 프로그램의 성능이 “CPU 연산”에 의해 주로 결정되는 작업으로 계산 자체가 복잡하고 많아서, CPU 사용이 100%에 가까울 정도로 바쁠때, 이를 CPU바운드 작업이라고 한다. 먼저, Python에서의 멀티 스레딩은 병렬 계산으로 진행되지 않기때문에, CPU바운드 작업의 케이스에 대해서는 개선효과가 없다.\n앞서 설명했듯, Python에서는 GIL로 인해 멀티스레딩 이라 하더라도, 직렬로 처리된다.\n예를들어, 1부터 10억까지 더하는 Python 반복문 프로세스를 2개의 스레드로 나눠서 작업하더라도 GIL때문에\n한번에 하나의 스레드만 사용되고, 번갈아가며 작업이 수행된다. 결국, 싱글 스레드와 큰 차이가 없게되는 것이다.\n다만, I/O바운드 작업에서는 효과가 있다.\n파일 읽기/쓰기, 소켓 네트워크 통신, DB접근, time.sleep() 와 같이 I/O대기나 블로킹이 길다면, 해당 스레드는 GIL을 잠시 해제하거나, 최소한 CPU를 점유하지 않고, 대기 상태가 된다.\n그사이 다른 스레드로 전환을 통해 CPU 를 잡고 작업할 수 있으므로, 동시성(Concurrency)을 사용할 수 있게 된다.\n예를들어, 웹서버를 구현할떄, 파이썬으로 멀티스레드를 활용하게 되면, 하나의 요청이 DB의 응답을 기다리는 동안,\n다른 요청을 처리할 수 있기 때문에 단일 스레드 보다 훨씬 응답성이 좋아질 수 있다.\nThread 전환은 언제되는가?(작업 분배) # 마지막으로, 그렇다면 직렬 방식으로 계산되는 Python의 멀티스레드에서 각 스레드간의 스위칭은 어떤 방식, 기준으로 이루어지는걸까?\n실제 실행은 시분할(time-slicing)방식으로 이루어진다. 즉, 하나의 스레드가 잠시 실행되었다가, 다른 스레드로 전환되는데,\n1) 일정시간(또는 일정 바이트코드 수)이 지났을때,\n2) 스레드가 I/O대기 이거나, time.sleep()과 같은 블로킹 동작으로 GIL을 해제할때,\n3) 스레드가 명시적으로 threading이나, C확장 라이브러리 수준에서 GIL을 잠시 풀때가\n그에 해당한다.\n위의 시점을 기준으로 GIL이 다른 스레드로 넘어가게되고, 그 결과 스레드의 전환(Thread Switching)이 일어난다.\n참고로, 스레드 전환의 기준시간은 sys.getswitchinterval(), sys.setswitchinterval(value) 으로 확인하고 변경할 수 있다.\n기본 값은 0.005s정도이며, 해당 시간을 초과할 경우 다른 스레드가 GIL을 획득하게 된다.\nThread를 사용함에 있어 주의해야할 점 # 반면, Thread를 사용함에 있어, 단점 또는 주의할 점도 존재한다.\n동기화 문제(Synchronization) 각 스레드가 하나의 자원(변수, 리스트, 파일, DB 등)에 대해 동시 참조를 통한 수정이 발생할 경우, 접근/수정의 순서가 엉켜 예상치 못한 결과가 발생할 수 있다. 동기화 기법 Lock 또는 뮤텍스(Mutex) : 동시에 한 스레드만 임계 구역(critical section)에 들어가도록 보장하는 방법 세마포어(Semaphore) : 카운팅이 가능한 락 개념(동시에 N개 스레드만 접근 가능) 조건 변수(Condition Variable) : 특정 조건을 만족할 때까지 스레드가 대기하고, 조건이 만족되면 알림(notifiy)으로 깨운다. 이외에도 Event, Pipeline, Queue 기반의 병렬 처리 등의 기법이 존재한다. 데드락(Deadlock) 데드락이란, 서로 다른 스레드가 서로가 가진 자원을 기다리며 무한 대기 상태에 빠지는 현상이다. 예를들어 스레드 A가 락1을 획득하고, 락2를 기다리는 중이고, 스레드 B가 락2를 획득하고, 락1을 기다리는 상황(\u0026ldquo;교착 상태\u0026rdquo;) 이 상태에선 양쪽 모두 락을 얻지 못해 진행이 불가능하므로, 프로그램이 멈춰버린다. lockA = threading.Lock() lockB = threading.Lock() def thread_a(): with lockA: time.sleep(0.1) # 일부러 딜레이를 줘서 스레드B가 lockB를 먼저 잡게 만듬 with lockB: print(\u0026#34;A 작업 수행\u0026#34;) def thread_b(): with lockB: time.sleep(0.1) with lockA: print(\u0026#34;B 작업 수행\u0026#34;) thread_a는 LockA를 잡은 뒤 LockB를 잡으려 하고, thread_b는 lockB를 잡은 뒤 lockA를 잡으려한다. 이때, 둘다 상대방이 가진 락을 기다리는 상태가 돼 무한정 정지가 된다.\n스레드 수 관리 스레드가 너무 많을 때의 문제 문맥 전환(Context Switch) 오버헤드 : 스레드가 늘어날수록 CPU가 실행 스레드를 전환하는 데 드는 비용이 커지는 문제 메모리 사용량 증가 : 각 스레드는 고유 스택 메모리를 가지고, 스레드 구조체 등의 관리 리소스를 차지하는데, 이로 인한 리소스 비용 데드락*동기화 문제가 복잡해짐 : 스레드가 많아지면 동기화 로직이 늘어나고, 디버깅이 어려워진다. 해결방법 Thread Pool 일정 개수의 스레드를 미리 만들어두고, 작업이 들어올 때 스레드 풀에서 유휴 스레드를 가져와 일을 처리하는 방식 작업이 많아도 스레드 수를 무작정 늘리지 않고, 적절한 개수로 제한하는 방식 비동기 I/O나 이벤트 기반 I/O가 많은 작업은 스레드 대신 비동기(Async/Await), 코루틴 방식으로 동시성을 확보하는 경우도 많음. 디버깅의 복잡성 멀티 스레드 디버깅의 어려움 실행 순서가 매번 다름 : 스레드 간 실행 타이밍이 예측 불가능하므로, 특정 타이밍에서만 발생하는 버그(타이밍 버그)가 재현이 어렵습니다. 경쟁상태(Race Condition) 재현 : 로컬 환경에서 수십 번 테스트해도 안 나타나다가, 실제 환경(고사양 서버)에서만 발생할 수도 있음 Lock 및 공유 자원 추적 : 어떤 스레드가 어떤 자원을 언제 잡고 놓는지 추적하기가 복잡합니다. 개선방안 로깅(Logging)강화 : 각 스레드가 자원 획득/해제 시점을 자세히 로그로 남겨, 문제가 생겼을 때 역추적이 가능하도록 한다. 단위+스테레스 테스트 : 스레드 관련 코드를 가능한 작은 단위로 테스트하고, 동시 접속/스트레스 테스트툴을 이용해 버그 조기 발견 Deadlock Detection : 특정 시점에 어떤 락이 누구에게 점유되어 있는지 확인하는 모니터링(디버거, 프로파일러 툴 사용) 알고리즘적 해결 : 불변 객체(immutable)나 Lock-Free)알고리즘을 통해 동기화 부담을 줄이는 방식 오늘은 Python에서의 Thread의 역햘 및 효과에 대해 관련 개념들과 함께 알아보았다.\nThread는 CPU자원을 효율적으로 사용하여, 프로그램의 성능을 향상시킬 수 있는 좋은 수단이 되지만,\n실제 체감할 수 있는 성능 향상을 위해서는 Python하에서의 Thread의 동작 원리를 이해하고, 실제로 효과를 거둘 수 있는 케이스에 적절히 활용할 수 있어야 하겠다.\n","date":"2025.03.31","externalUrl":null,"permalink":"/posts/109/","section":"글","summary":"","title":"Python에서 Thread는 어떻게 사용하는가?","type":"posts"},{"content":"","date":"2025.03.31","externalUrl":null,"permalink":"/tags/thread/","section":"Tags","summary":"","title":"Thread","type":"tags"},{"content":"","date":"2025.03.31","externalUrl":null,"permalink":"/tags/%EC%8A%A4%EB%A0%88%EB%93%9C/","section":"Tags","summary":"","title":"스레드","type":"tags"},{"content":"","date":"2025.03.07","externalUrl":null,"permalink":"/tags/debian/","section":"Tags","summary":"","title":"Debian","type":"tags"},{"content":"","date":"2025.03.07","externalUrl":null,"permalink":"/tags/install-python/","section":"Tags","summary":"","title":"Install Python","type":"tags"},{"content":"","date":"2025.03.07","externalUrl":null,"permalink":"/tags/linux/","section":"Tags","summary":"","title":"Linux","type":"tags"},{"content":"","date":"2025.03.07","externalUrl":null,"permalink":"/categories/mlops--infra/","section":"Categories","summary":"","title":"MLOps \u0026 Infra","type":"categories"},{"content":"1) 배포판 확인\ncat /etc/os-release # 출력예시 PRETTY_NAME=\u0026#34;Ubuntu 22.04.3 LTS\u0026#34; NAME=\u0026#34;Ubuntu\u0026#34; VERSION_ID=\u0026#34;22.04\u0026#34; VERSION=\u0026#34;22.04.3 LTS (Jammy Jellyfish)\u0026#34; VERSION_CODENAME=jammy ID=ubuntu ID_LIKE=debian HOME_URL=\u0026#34;https://www.ubuntu.com/\u0026#34; SUPPORT_URL=\u0026#34;https://help.ubuntu.com/\u0026#34; BUG_REPORT_URL=\u0026#34;https://bugs.launchpad.net/ubuntu/\u0026#34; PRIVACY_POLICY_URL=\u0026#34;https://www.ubuntu.com/legal/terms-and-policies/privacy-policy\u0026#34; UBUNTU_CODENAME=jammy 배포판 : debian\nUbuntu ver : 22.04\n2) 의존 패키지 설치\nsudo apt update \u0026amp;\u0026amp; sudo apt upgrade -y sudo apt install -y build-essential zlib1g-dev libssl-dev libreadline-dev \\ libsqlite3-dev libbz2-dev libffi-dev liblzma-dev 3) Python 3.10.12 소스코드 Download\ncd /usr/src sudo wget https://www.python.org/ftp/python/3.10.12/Python-3.10.12.tgz sudo tar xvf Python-3.10.12.tgz cd Python-3.10.12 4) Python 컴파일 및 설치\nsudo ./configure --enable-optimizations sudo make -j$(nproc) # 병렬 빌드 (CPU 코어 수만큼 사용) sudo make altinstall # 기존 Python과 충돌 방지 5) 설치완료 확인\npython3 --version # 출력예시 Python 3.10.12 ","date":"2025.03.07","externalUrl":null,"permalink":"/posts/108/","section":"글","summary":"","title":"Python 3.10.12 install - At distribution #Debian #Ubuntu 22.04","type":"posts"},{"content":"","date":"2025.03.07","externalUrl":null,"permalink":"/tags/ubuntu/","section":"Tags","summary":"","title":"Ubuntu","type":"tags"},{"content":"오늘은 올해 발행되어, RAG 시스템의 개선 아이디어를 제시한 \u0026lsquo;RAPTOR\u0026rsquo; 논문에 대해 자세히 리뷰해보려 한다.\n0. 어원 # 먼저, RAPTOR의 어원을 먼저 살펴보자.\n페이퍼 타이틀인 **\u0026ldquo;RECURSIVE ABSTRACTIVE PROCESSING FOR TREE-ORGANIZED RETRIEVAL\u0026rdquo;**를\n직역하면, \u0026ldquo;트리로 구성된 검색을 위한 재귀적 추상 처리\u0026quot;로 번역할 수 있는데,\n뒤에 설명하겠지만, 해당 논문은 RAG를 위한 데이터 구축 방식 및 검색 방법론을 설명하고있고, 여기서는 **\u0026lsquo;트리\u0026rsquo;**와 **\u0026lsquo;재귀적 추상\u0026rsquo;**이라는 키워드에 대해서만 주목하고 넘어가면 될 것 같다.\n만약, RAPTOR의 동작방식에 대해서만 빠르게 이해하고자한다면, 아래의 \u0026ldquo;2.그렇다면, RAPTOR란 무엇인가?\u0026rdquo; 의\n프로세스 부분만 읽어도 무방하다.\n1. 배경 # 이미지 출처 : RAGFlow\n기존 Native RAG의 문제점? # RAPTOR는 기존 RAG시스템의 아래 두가지 문제를 해결하기 위해 제안되었다.\n첫번째 - 파편화된 참조 정보로 인한 맥락 이해의 어려움\n최근 LLM의 단점을 보완할 수 있는, RAG(검색 증강 생성)기법이 소개되면서, 이를 활용하여 다양한 분야에서 domain knowledge를 활용한 연구 및 서비스 개발이 가능해졌다.\nRAG 시스템은 보통 User Query에 대해서, 상위 K개의 참조정보를 \u0026lsquo;추출\u0026rsquo;한 후, 해당 참조 데이터를 참고해서 높은 객관성을 가진 답변을 생성해내는 원리로 동작한다.\n이런 RAG의 특징을 통해, LLM의 고질적인 문제였던 hallucination과 domain knowledge를 참조하지 못하는 문제들을 상당히 해결해내었지만, 추가적인 성능 개선의 필요성이 발생했다.\n예를들어, **\u0026ldquo;신데릴라가 어떻게 행복한 결말을 맞이 했는가?\u0026rdquo;**라는 질문에 답한다고 가정해보자.\nNative RAG에서는 먼저 주어진 Vector DB내에서 위 문장에 대한 답변을 제공할때 참고할 수 있는\nK개의 documents를 추출한다.\n각 document는 전체 정보중 유사성이 높은 일부 데이터들로 추출될것이다.\n가령,\n[\n\u0026ldquo;호박마차를 탔을때, 신데렐라는 유리구두를 잃어버렸다는것을 알게되었습니다\u0026rdquo;,\n\u0026ldquo;신데렐라의 언니들은 저마다 화려한 옷을 입고, 반짝이는 장신구로 치장했습니다\u0026rdquo;,\n\u0026ldquo;그날은 왕자님이 배우자를 찾기 위한 자리였습니다\u0026rdquo;,\n\u0026hellip;\n]\n위와 같은 파편화된 문장이 주어졌을때,\n최초의 질문이었던**\u0026ldquo;신데릴라가 어떻게 행복한 결말을 맞이 했는가?\u0026rdquo;**라는 질문에 제대로 답변할 수 있을까?\u0026hellip;\n위 예시와 같이 RAG시스템은 사용자의 질문에 적합한 참고 데이터를 통해 기반 정보를 확대할 수 있다는 장점이 있지만,\n전체 정보 또는 특정 sector의 맥락정보를 이해하지 못하고, 파편화된 데이터만을 통해서 피상적 답변의 추출만 가능하다는 한계점을 가지고 있다.\n한가지 실제 사례를 추가로 보자면, 세법문서의 경우, 헌법, 법률, 시행력 등 하위규정과 같이 트리구조로 계층화되어 조직화된 경우가 많고, 과학 논문 또한 학문 분야와 주제별로 하위 내용을 형성한다.\n대부분의 도메인 지식들은 이와 같이 계층적으로 구조화 되어있는데, 이와 같이 많은 정보가 계층적으로 조직된 경우, 상위 노드에서 중요한 내용을 요약하고, 하위에서 세부 사항을 탐색하는 방식이 효과적이다.\n이는 보통 인간이 정보를 탐색하는 방식인데, 예를들어, 세법에서도 부가가치세법, 법인세법 등 다양한 법령 카테고리가 나눠진 후,\n부가가치세법 하위에 의제매입에 대한 조항, 수출입 물품에 대한 부가세 적용 조항과 같이\n하위 구조가 어이지는 형태로 구성되어있다.\n사람은 계층화된 정보를 상위부터 의미를 파악하여 범위를 좁히며 의미를 파악해 나갈 수 있지만,\n기존의 RAG 시스템은 계층적 구조를 활용하지 못하고,\n키워드를 기반으로 개별적 단위로 탐색을 진행한다는 문제점을 가지고 있다.\n두번째 - 대규모 데이터 처리 문제\n\u0026lt;RAG 시스템하에서 32k의 context를 input했을때, 하나의 질문에 소요되는 시간과 비용 측정 → 질문당 30달러, 2~7분 소요\u0026hellip;.서비스가 불가한 수준\u0026hellip;.\u0026gt;\n기존 RAG 시스템은 각 문서를 개별적으로 처리하거나 단순 키워드 매칭에 의존하는 경우가 많아,\n추출되는 정보의 양이 방대하기 때문에, 정보를 많이 참고할수록 속도가 느려지고, 검색 결과의 품질이 떨어진다.\n이러한 단점을 개선하기 위해, 임의로 정의한 특정 길이 만큼만의 정보만 참조할 경우에도 또한 품질이 떨어질 수 밖에 없다.\n세번째 - \u0026ldquo;요즘에 많이 나오고있는 큰 input length(32k 등..)를 지원하는 모델에서 전체 문서를 input하면 이런 문제가 해결되지 않는가?\u0026rdquo; # 다만, 최근 하드웨어 및 알고리즘의 발전으로 모델이 한번에 처리할 수 있는 context의 길이가 확장되면서,\n검색시스템의 필요성에 대한 의문이 제기되기도 한다.\n위의 예시와 같이 대량의 input length를 허용하는 모델에 전체 문서를 통째로 input하면, 파편화된 정보로 인해 전체 문맥을\n이해하지 못하는 문제도 해결되지않을까라는 반론을 제시할 수 있을것이다.\n하지만, 근본적으로, 32k모델과 같이 큰 length의 text를 한번에 input한다고 해도, 전체 문서 내에서 적절한 처리없이,\n높은 탐색 성능을 보장할 수는 없다.\n기존 RAG 동작원리\nLiu et al. (2023)과 Sun et al(2021) 논문에서는 모델은 주어진 매우 큰 크기의 context에 대해 정보를\n충분히 활용하지 못하는 경향을 가진다는 연구결과를 발표한 바 있다.\n특히, long context가 주어질 수록 성능이 저하되는 특징도 함께 주장되었다.\n이뿐아니라 만약 수십, 수백만개의 토큰으로 이루어진 long context를 사용하는것은 비용과 Latency측면에서도\n매우 큰 리스크를 가진다.\n결국, 전체 문서의 맥락적 의미를 파악하고, 지식을 통합해야하는 질문에 대해 답변하기 위해서는 기존과 다른 새로운 방법론이 필요하고,\n이번에 소개할 논문은, RAPTOR가 그 대안이 될 수 있음을 주장하고있다.\n2. 그렇다면, RAPTOR는 무엇인가? # https://arxiv.org/pdf/2401.18059v1\nRAPTOR의 컨셉을 한마디로 요약하면, **\u0026ldquo;하위에서 상위 Depth로 이어지는 요약 트리구조를 생성****하고 이를 참고\u0026rdquo;**하는 기법을 의미한다.\nRAPTOR의 진행 프로세스에서의 핵심은, clustering과 summarization을 통한 계층적 요약구조인데,\n이 개념이 사실 처음 나온 아이디어는 아니다.\nAuthor IDEA Angelidis \u0026amp; Lapata (2018) 재귀적 요약을 컨텍스트 요약 기법으로 사용하면 문서의 요약된 뷰를 제공하여 콘텐츠에 더 집중정으로 참여 가능.. → 문서의 내용을 축약하는 방식으로, 전체적 Context를 반영할 수 있음에 대한 아이디어 Gao et al.(2023) 요약/스니펫 모델은 구절의 요약과 스니펫을 사용하여, 대부분의 데이터세트에서 정확성을 향상시키지만, 때로는 손실이 많은 압축수단일 수 있음 → 문서 요약은 정확성을 높이지만, 압축 과정에서 디테일한 세부 정보가 소실될 수 있음을 언급 Wu et al. (2021) 재귀적 추상 요약 모델(재귀적으로 추상화된 요약을 생성 : map-reduce)은 작업 분해(task decomposition)를 사용하여, 더 작은 text chunk를 요약하고 나중에 통합하여 더 큰 섹션의 요약을 만듦. 이 방법은 더 광범위한 테마를 포착하는 데 효과적이지만 세부적인 내용을 놓칠 수 있습니다. → 마찬가지로 광의의 의미를 잘 파악하겠지만, 세부정보를 누락할 가능성을 언급 LlamaIndex(Liu, 2022) 유사한 방식으로 인접한 Text chunk를 요약하지만, 중간 노드를 유지하여, 다양한 수준의 세부 정보를 저장하고 세부적인 내용을 유지함으로써 이 문제를 완화합니다. 그러나, 두 방법 모두 인점한 노드를 그룹화하거나 요약하기 위해 인접성에 의존하기 때문에 텍스트 내에서 RAPTOR로 찾아서 그룹화할 수 있는 먼 상호의존성을 간과할 수 있습니다. → 말그대로, 인접한 chunk간의 관련성을 그룹화하는데, 이는 멀리 떨어진 문맥간의 의존성, 관계성을 무시하는 결과를 보이게 될거임.. 위와 같이 유사한 여러건의 연구결과를 통해 종합해보면,\n1) 전체적 문맥정보를 파악하기 위해서는 전체 또는 sector별 요약 정보가 도움이 될 수 있다.\n2) 요약 정보만 남기게 될 경우, 세부적 의미를 누락할 위험이 있기 때문에 하위정보와 요약정보를 함께 활용해야한다.\n3) 전체 정보공간(전체 documents)에서 흩어져있는 관련 정보들을 연결지어 온전한 정보로 활용할 필요가 있다.\n세가지 인사이트를 도출할 수 있는데,\n이 세가지를 모두 충족할 수 있는 방향으로 고안된 방법론이 RAPTOR라고 보면된다.\n자, 그럼 위 3가지 인사이트를 어떻게 반영했는지, 아래 RAPTOR 작업 프로세스를 보면서 살펴보자. ㄱㄱ~!\n1) Chunking 전체 텍스트를 길이, 의미 단위로 Chunking을 진행한다(첫번째 그림에서 Leaf layer에 해당) - 최대 100 token 단위로 chunking - 각 chunk는 마구잡이로 끊으면 안되고, 의미적으로 맥락적, 의미적 일관성이 유지되는 하나의 context로 Chunking해야한다**(중요!)** - 만약, 한 문장이 100토큰 제한을 초과하면, 문장을 자르는 대신 전체 문장을 다음 청크로 이동한다. 2) Embedding Chunking 된 각 문장들에 대한 embedding vector를 생성한다. - 의미적으로 정확한 Vector추출을 위해 높은 성능의 임베딩 모델을 필요로하며, 논문에서는 SBERT(BM24, DPR 비교우위)가 좋은 성능을 보인것을 확인되었다. - THINK : 실제 서비스 환경에서 적용할때는 Domain Knowledge가 최대한 반영된 Embedding Model을 확보하고 있는가가 정교한 Cluster를 구성하는데 필수 조건이 될 것으로 보인다. 3) Clustering Chunk Pool을 대상으로 Clustering을 진행하여 유사문장들간의 Group을 형성한다. (두번째 그림 참고) - 1개 문서는 여러 클러스터에 중복 포함될 수 있다. - 클러스터 개수는 어떻게 설정? - Gaussian Mixture Model을 통해 Bayesian InformationCriterion, BIC를 계산하여 개수를 확정함 - THINK : 적절한 내용분류 여부를 확인하기 위해 클러스터링 결과에 대한 중간 검증과정이 매우 중요할 것으로 보인다. 4) Summarization by LLM (중요) LLM을 통해 각 클러스터의 \u0026lsquo;요약 문장\u0026rsquo;을 생성한다. - 논문에서는 GPT계열에서 가장 좋은 성능을 보였다고하며, GPT4에서 역시나 최고 성능을 확인 하였다고 한다. 2~4) 종합 : TREE 구축 METHOD 자, 여기까지 2~4번과정은 결국, 상세정보와 축약정보를 골고루 담고있는 Tree 를 구축하기 위한 과정이었다. 여기서 Tree 구축에 대한 조금 더 디테일한 방법론을 살펴보자. 1) GMM(가우시안 혼합모델) - 다양한 클러스터에 걸쳐 데이터 포인트의 분포를 모델링한다. - 모델의 베이지안 정보 기준(BIC)을 평가하여, \u0026lsquo;최적의 클러스터\u0026rsquo; 개수를 결정한다. 2) UMAP(Uniform Manifold Approximation and Projection) - 고차원 데이터의 차원을 축소하는 성격을 통해 클러스터링을 지원 - UMAP은 데이터 포인트의 유사성에 기반하여 자연스러운 그룹화를 강조하는데 도움을 준다. 3) Similarity Score의 Threshold를 반영한 클러스터링 구성 - 예를들어, \u0026lsquo;가\u0026rsquo;문서가 A, B, C클러스터에 각각 포함되었을떄, 0.7(사용자 지정)이상의 유사도를 가진 A, B 클러스터에는 포함하고, 0.41의 유사도를 가진 C클러스터에는 제거하는방식으로, 유사도를 클러스터에 반영하는 기법 4) 지역 및 전역 클러스터링 - 다양한 규모에서 데이터를 분석하는데 사용된다. - 데이터 내의 세밀한 패턴과 더 넓은 패턴 모두를 효과적으로 포착한다. - 예를들어, 세부적인 내용을 담고있는 클러스터는 B, 전체적인 맥락정보(추상적인 정보)를 담고있는 클러스터는 C에 클러스터링 되도록 처리하여, 전역적인 정보와 지역적인 정보를 모두 담을 수 있게 한다. 5) 임계값 설정 - GMM의 맥락에서 클러스터 멤버십을 결정하기 위해 적용된다. - 확률 분포를 기반으로 한다.(데이터 포인트를 \u0026gt;= 1 클러스터에 할당) 5) Flatten(Collapsed Tree Structure) 위에서 생성한 Tree구조의 데이터를 그대로 사용하지 않고, Flatten하여 단일 layer에서 선택하도록 처리한다. - 이와 같은 방법을 사용하는 이유는 Flatten된 전체 chunk들은 다양한 계층(세부정보가 있는 하위 정보와, 추상적 요약 정보가 포함되어있는 상위 정보가 동일한 조건에서 Retrieval Pool을 형성)의 정보가 섞여있게된다. 이때, 사용자의 질문이 추상적이거나 세부적이거나에 관계없이 Pool에서 질문에 적절한 Depth의 정보를 검색할 수 있게 된다. VECTOR DB 생성 완료!!!\n6) Reference Context 검색 및 생성 - 이제, User Query에 적절한 Reference Context를 검색(추출)하고, 최종적으로 LLM에 답변을 얻기위해 Input할 \u0026lsquo;Reference Context\u0026rsquo;를 생성한다. - User Query(Vectorization)가 주어질 경우, 위에서 생성했던 Flatten된 VectorDB에서 질문과 가장 유사한 참조 문서를 검색한다. - (중요) 그리고, 검색된 노드의 document를 Reference Context로 추가하는데, 이 과정을 모델의 max context length에 도달할때까지 반복하며 추가할 수 있다. 이렇게 되면, 축약 또는 하위의 디테일한 정보, 즉 알짜정보만 반복적으로 추출할 수 있게된다. - (중요) 다만, 앞서 말했듯이, LLM에 input되는 context가 길어질 수록 LLM의 참고 답변 품질이 낮아지는데, 논문에서는 2,000token정도가 가장 적절한 Max Length로 이야기하고있다. 따라서 2,000token에 도달할때 까지 유사문장을 depth관계없이 다양하게 수집하게된다 7) Answer 마지막으로, 2,000토큰을 context Max length 기준으로 두고, 제공된 reference context와 User Query를 참고하여 LLM은 최종 답변을 출력하게 된다. 전체 Flow\n3. RAPTOR 효과 # Since RAPTOR with SBERT has the best performance\n\u0026hellip;\n\u0026lt;NarrativeQA Performance With + Without RAPTOR:\u0026gt;\n\u0026lt;QuALITY and QASPER Performance With + Without RAPTOR\u0026gt;\n\u0026lt;Controlled comparison of F-1 scores on the QASPER dataset, using three different language models (GPT-3, GPT-4, UnifiedQA 3B) and various retrieval methods\u0026gt;\nRetrieval-augmented language models can better adapt to changes in world state and incorporate long-tail knowledge. However, most existing methods retrieve only short contiguous chunks from a retrieval corpus, limiting holistic understanding of the overall document context. We introduce the novel approach of recursively embedding, clustering, and summarizing chunks of text, constructing a tree with differing levels of summarization from the bottom up. At inference time, our RAPTOR model retrieves from this tree, integrating information across lengthy documents at different levels of abstraction. Controlled experiments show that retrieval with recursive summaries offers significant improvements over traditional retrieval-augmented LMs on several tasks. On question-answering tasks that involve complex, multi-step reasoning, we show state-of-the-art results;for example, by coupling RAPTOR retrieval with the use of GPT-4, we can improve the best performance on the QuALITY benchmark by 20% in absolute accuracy.\n\u0026lt;평가 데이터\u0026gt;\nNArrativeQA(Ko cisk y et al.2018): 책과 영화에 대한 자유 텍스트 응답 질문 벤치마크 QASPER(Dasigi et al. 2021) : 과학 논문에 대한 질의응답 데이터셋으로, 논문의 복잡한 내용을 이해하고 해당 내용을 기반으로 질문에 답변하는 AI 모델을 평가하기 위해 개발된 데이터 셋 QuALITY(Pang et al. 2022) : 긴 형식의 입력 문서를 기반으로 질문에 답하는 AI 모델의 이해 및 추론 능력을 평가하기 위한 벤치마크 데이터셋으로, 단순 정보 추출이 아니라 문맥의 전체적인 이해와 추론이 요구되는 질문을 제공 함 **논문에서는 RAPTOR를 다양한 케이스에서 **QASPER, QuALITY 벤치마크를 통해****성능을 비교 평가했다.\n→ NArrativeQA 벤치마크를 기준으로, SBERT, BM25, DPR중에서는 SBERT가 RAPTOR를 함께 사용했을때, 좋은 성능을 보여주었다.\n→ 긴 입력 문서 기반 답변(QuALITY)에서 모든 임베딩 알고리즘이 RAPTOR를 함께 사용했을때, 단독으로 추론한 것 보다 더 좋은 성능을 보여주었다.\n→ 논문기반 답변 벤치마크(QASPER)에서 BM25 vs DPR vs SBERT\u0026amp;RAPTOR 를 각각 다양한 LLM과 함께 사용했을때, SBERT\u0026amp;RAPTOR를 GPT4와 함께 사용했을때 가장 좋은 성능을 보였다. (논문에 표현된 지표 상, 약 20%정도의 성능 향상을 확인할 수 있다.)\n4. RAPTOR 구현 시, 주의할 점 (참고) # Teddy Note (Youtube)\nRAPTOR를 구성할때, 가장 중요한점은 Tree를 잘 구축하는것이다.\nTree에 요약된 부모 노드의 context가 하위 노드의 정보를**\u0026lsquo;잘~요약\u0026rsquo;**했을때, Clustering 성능과, 최종 LLM의 정보 Extraction 성능을 높아질 수 있기 때문이다!\n요약된 정보의 품질이 낮을 경우, 이후 재귀적으로 진행되는 클러스터링 결과 및 부모노드의 퀄리티는 완전히 다른 결과를 반영할 수 있다.\n이 내용은, 테디노트 RAPTOR 영상의 한 구독자 댓글에서도 해당 인사이트를 확인할 수 있다.\n이 부분은 추후 RAPTOR구현 시, Vectorization 및 요약에 사용할 모델의 실제 성능을 잘 평가, 선택할 필요가 있음을 알 수 있다.\n5. 결론 및 OneAI 활용 방안 # 결론 RAPTOR는 파편화된 document를 의미단위로 축약처리하여, 추상적인 Query에도 적절한 참고 정보를 찾을 수 있는 검색 알고리즘이다. 위치와 관계없이 문서내 흩어진 관련 정보를 모아 새로운 참조 데이터를 생성할 수 있다. 상세 정보와 추상정보를 모두 포함하여, 다양한 Depth의 Query에도 강건한 검색 성능을 가진다. max context내에 질문과 밀접한 관련이 있는 참조 정보만으로 선택적으로 추출이 가능하다. 오늘은 RAPTOR논문에 대해서 자세히 살펴보았다.\nRAPTOR는 복잡한 내부 프로세스 없이, 클러스터링과 요약으로 이루어진 Tree 구조의 데이터 증강을 통해\n손쉽게 RAG 참조 데이터의 퀄리티를 높일 수 있는 방식으로 보인다.\n현재 RAPTOR는 FAISS와 Langchain을 통해서 간편하게 구현이 가능하기때문에,\n기존 서비스에 이미 반영된 RAG시스템을 빠르게 대체할 수 있을것으로 보이고,\n교체에 드는 리소스보다, 다양한 Depth의 질문에 대한 답변 성능 개선 효과가 더 클 것으로 예상된다.\n나도 얼른 서비스하고 있는 챗봇에 적용해보고 추후 반영 후기도 한번 포스팅 해보도록 하겠다.\n","date":"2024.11.19","externalUrl":null,"permalink":"/posts/106/","section":"글","summary":"","title":"[RAG] RAPTOR(Recrusive Abstractive Processing for Tree-Organized Retrieval) Parth Sarthi et al. 2024","type":"posts"},{"content":"","date":"2024.11.19","externalUrl":null,"permalink":"/tags/llm/","section":"Tags","summary":"","title":"Llm","type":"tags"},{"content":"","date":"2024.11.19","externalUrl":null,"permalink":"/categories/paper-review/","section":"Categories","summary":"","title":"Paper Review","type":"categories"},{"content":"","date":"2024.11.19","externalUrl":null,"permalink":"/tags/raptor/","section":"Tags","summary":"","title":"Raptor","type":"tags"},{"content":"","date":"2024.11.19","externalUrl":null,"permalink":"/tags/recrusive-abstractive-processing-for-tree-organized-retrivall/","section":"Tags","summary":"","title":"Recrusive Abstractive Processing for Tree-Organized Retrivall","type":"tags"},{"content":"","date":"2024.11.19","externalUrl":null,"permalink":"/tags/retrival/","section":"Tags","summary":"","title":"Retrival","type":"tags"},{"content":"","date":"2024.11.19","externalUrl":null,"permalink":"/tags/sllm/","section":"Tags","summary":"","title":"Sllm","type":"tags"},{"content":"","date":"2024.11.19","externalUrl":null,"permalink":"/tags/slm/","section":"Tags","summary":"","title":"Slm","type":"tags"},{"content":"","date":"2024.11.19","externalUrl":null,"permalink":"/tags/tree/","section":"Tags","summary":"","title":"Tree","type":"tags"},{"content":"","date":"2024.10.18","externalUrl":null,"permalink":"/tags/adapter/","section":"Tags","summary":"","title":"Adapter","type":"tags"},{"content":"","date":"2024.10.18","externalUrl":null,"permalink":"/tags/lora-fine-tuning/","section":"Tags","summary":"","title":"Lora Fine-Tuning","type":"tags"},{"content":"","date":"2024.10.18","externalUrl":null,"permalink":"/tags/peft/","section":"Tags","summary":"","title":"Peft","type":"tags"},{"content":"","date":"2024.10.18","externalUrl":null,"permalink":"/tags/rank/","section":"Tags","summary":"","title":"Rank","type":"tags"},{"content":"","date":"2024.10.18","externalUrl":null,"permalink":"/tags/%EB%A1%9C%EB%9D%BC/","section":"Tags","summary":"","title":"로라","type":"tags"},{"content":"","date":"2024.10.15","externalUrl":null,"permalink":"/tags/contrastive-learning/","section":"Tags","summary":"","title":"Contrastive Learning","type":"tags"},{"content":"","date":"2024.10.15","externalUrl":null,"permalink":"/tags/positive-pair/","section":"Tags","summary":"","title":"Positive Pair","type":"tags"},{"content":"","date":"2024.10.15","externalUrl":null,"permalink":"/tags/simcse/","section":"Tags","summary":"","title":"Simcse","type":"tags"},{"content":"","date":"2024.10.15","externalUrl":null,"permalink":"/tags/transfer-learning/","section":"Tags","summary":"","title":"Transfer Learning","type":"tags"},{"content":"","date":"2024.10.15","externalUrl":null,"permalink":"/tags/%EC%9E%84%EB%B2%A0%EB%94%A9%EB%AA%A8%EB%8D%B8/","section":"Tags","summary":"","title":"임베딩모델","type":"tags"},{"content":"","date":"2024.10.15","externalUrl":null,"permalink":"/tags/%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D/","section":"Tags","summary":"","title":"파인튜닝","type":"tags"},{"content":" Linux vi editor\n1. vi 편집기 열기 # vi filename 2. CRUD # 1) 입력 모드 전환\ni # 커서 앞에서 입력 2) 텍스트 입력 후 저장 및 종료\nESC # 명령 모드로 전환 :wq # 저장하고 종료 3) 읽기(Read) : 파일을 읽고 탐색하는 방법입니다. 기본적으로 파일을 열면 내용을 탐색할 수 있습니다.\nh #왼쪽으로 이동 j #아래로 이동 k #위로 이동 l #오른쪽으로 이동 Ctrl + f #한 화면 아래로 이동 Ctrl + b #한 화면 위로 이동 G #파일 끝으로 이동 gg #파일 시작으로 이동 4) 찾기 : 특정 단어 검색 \u0026lsquo;/\u0026rsquo; 를 사용하여 파일 내에서 특정 단어를 검색할 수 있습니다.\n/검색어 #파일에서 검색어를 찾음 n #다음 검색 결과로 이동 N #이전 검색 결과로 이동 5) 수정(Update) : 파일의 내용을 수정할 때는 명령 모드에서 특정 명령어를 사용하여 수정할 수 있습니다.\n문자수정 r #현재 커서 위치의 문자를 다른 문자로 변경 R #입력 모드로 전환하여 여러 문자를 덮어씌움 단어수정 cw # 커서가 위치한 단어를 변경 (현재 단어 삭제 후 입력 모드 전환) C # 커서 위치부터 줄 끝까지 변경 6) 삭제(Delete)\n\u0026lt;문자 삭제\u0026gt; x # 현재 커서 위치의 한 문자 삭제 \u0026lt;단어 삭제\u0026gt; dw # 커서가 위치한 단어 삭제 \u0026lt;줄 삭제\u0026gt; dd # 커서가 위치한 줄 삭제 7) 저장 및 종료 명령\n:w # 파일 저장 :q # 파일 종료 :wq # 파일 저장 후 종료 :q! # 저장하지 않고 강제 종료 3. 사용 예시 # 1) 파일에 텍스트 추가 후 저장 # 1. 파일 열기:\nvi myfile.txt 2. 입력 모드로 전환:\ni # 입력 모드 진입 3. 텍스트 입력:\nHello, this is a new line. 4. 명령 모드로 전환:\nESC # 입력 종료 5. 저장 후 종료:\n:wq # 저장하고 종료 2) 특정 줄 삭제 # 1. 파일 열기:\nvi myfile.txt 2. 특정 줄로 이동:\n10G # 10번째 줄로 이동 3. 줄 삭제:\ndd # 현재 줄 삭제 ","date":"2024.09.10","externalUrl":null,"permalink":"/posts/98/","section":"글","summary":"","title":"[Linux]vi 탐색기 CRUD 기능 정리","type":"posts"},{"content":"","date":"2024.09.10","externalUrl":null,"permalink":"/tags/vi/","section":"Tags","summary":"","title":"Vi","type":"tags"},{"content":"","date":"2024.09.10","externalUrl":null,"permalink":"/tags/vi%ED%83%90%EC%83%89/","section":"Tags","summary":"","title":"Vi탐색","type":"tags"},{"content":"","date":"2024.09.10","externalUrl":null,"permalink":"/tags/%EB%A6%AC%EB%88%85%EC%8A%A4/","section":"Tags","summary":"","title":"리눅스","type":"tags"},{"content":"","date":"2024.09.10","externalUrl":null,"permalink":"/tags/%EB%A6%AC%EB%88%85%EC%8A%A4-vi/","section":"Tags","summary":"","title":"리눅스 Vi","type":"tags"},{"content":"","date":"2024.09.10","externalUrl":null,"permalink":"/tags/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%8E%B8%EC%A7%91/","section":"Tags","summary":"","title":"리눅스 편집","type":"tags"},{"content":"","date":"2024.05.28","externalUrl":null,"permalink":"/tags/agent/","section":"Tags","summary":"","title":"Agent","type":"tags"},{"content":"","date":"2024.05.28","externalUrl":null,"permalink":"/tags/chatbot/","section":"Tags","summary":"","title":"Chatbot","type":"tags"},{"content":"","date":"2024.05.28","externalUrl":null,"permalink":"/tags/function-call/","section":"Tags","summary":"","title":"Function Call","type":"tags"},{"content":"","date":"2024.05.28","externalUrl":null,"permalink":"/tags/langchain/","section":"Tags","summary":"","title":"Langchain","type":"tags"},{"content":"","date":"2024.05.28","externalUrl":null,"permalink":"/tags/llama-cpp/","section":"Tags","summary":"","title":"Llama-Cpp","type":"tags"},{"content":"","date":"2024.05.28","externalUrl":null,"permalink":"/tags/llama3/","section":"Tags","summary":"","title":"Llama3","type":"tags"},{"content":"","date":"2024.05.28","externalUrl":null,"permalink":"/tags/llama3-70b/","section":"Tags","summary":"","title":"Llama3 70b","type":"tags"},{"content":"오늘은 Llama-cpp와 Langchain을 이용해서 llama3 모델로 Function Call을 구현하는 과정에 대해 리뷰해보려고한다.\n구현 과정에서 여러 삽질을 좀 한 관계로, 비슷한 시도를 하는 분들이 있다면,\n시행착오를 줄였으면 하는 바램으로 진행과정을 정리해본다.\n1. 왜 Langchain을 사용했는가? # 최근 구현되고 있는 챗봇들의 경우, 단순 질문에 대한 답변뿐아니라, 유저의 지시사항을 반영(action)할 수 있는\n복합적인 동작과 기능을 제공하고 있다.\n이와 같이 유저의 의도에 맞는 정확한 피드백을 제공하기 위해서는 LLM, RAG, Function call을 포함한 다양한 기술을 통해 답변을 처리할 수 있는 Agent 방식을 기반으로 동작하도록 챗봇을 구현해야하는데, Agent 개발을 가장 편하게 구현할 수 있게 도와주는 대표적인 프레임워크로 Langchain을 많이 사용하고 있다.\nLangchain을 사용하는 가장 큰 이유는 LLM을 기반으로 Agent를 구성하는데 필요한 여러 기능 및 프로세스를 간편한 인터페이스로\n연결하는 다양한 기능들을 제공하기 때문이다.\n그리고, prompt, parser, model이 반복적으로 얽혀서 동작하게끔하는 복잡한 Agent구조를 Runnable 프로토콜을 통해\n명확한 코드형태로 구조화할 수 있게 도와준다.\n2. Open Source LLM을 Langchain과 함께 사용할때의 장점은? # 1) 너무 비싼 GPT4o API Token 비용의 문제를 해결할 수 있다. # 이번 PoC진행하게 된 가장 큰 계기는, 사내 챗봇 서비스에 대한 개선을 목적으로 진행하게 되었는데,\n현재 회사에서 제공중인 \bChatBot Agent에서는 Langchain과 GPT4o API를 이용해 Agent를 구성했다.\n당연하게도 GPT4o API의 토큰 비용 부담이 만만치 않았기에, 비용절감 대책이 필요했다.\nGPT4o는 현시점에서 가장 압도적인 성능을 보이는 모델이지만 Agent내에서\n모든 프로세스가 최고 성능의 모델을 필요로 하지는 않는 다고 판단했다.\n예를들어, Agent내에서 Function Call 동작을 위해서는 Query가 input될때마다,\n모든 Function(Tool)에 대한 정보를 모델에게 input해서 어떤 Tool을 사용해야할지 판단해야하는데,\n이 부분에서 매번 막대한 토큰이 사용된다.\n기존 오픈모델의 경우 모델 자체의 intelligence가 떨어지기 때문에, Tool과 Tool에 input할 Argument를 선별하는 성능이 떨어져\nFunction call 활용이 어려웠지만,\n최근 대폭 개선된 성능으로 주목받은 Llama3를 사용해서 Function Call에 적용한 사례가 있었다.\n다만, 한국어를 기반으로 구현한 사례가 많았다면 좋았겠지만 아직 레퍼런스가 거의 없었지만, 비슷한 고민을 하고있는 현업자들은 정말 많을것이라 생각했다.\n그래서, 이번 PoC를 통해, Function Call의 Tool 선택 및 Tool 실행 부분에 있어 Open Source LLM(Llama3) model의\n활용가능성을 직접 테스트해보고 공유해보고자 했다.\n* 한국어 오픈소스 모델을 기반으로 Langchain Function call을 구현한 거의 유일한 사례로, 유튜브의 테니노트님께서 구현해주신 내용이 있어 많이 참고했다. 다만, 저의 능력부족으로 일부 구현이 어려웠던 점이 있어, 대안을 찾아보려했던 몸부림을 아래에 정리해봤다.\n2) 보안 문제 해소 효과 # 비용문제 외에도 \bGPT4o API를 활용해 Agent를 구축할 경우, API Request에 포함되는 모든 데이터는 보안 이슈에서 자유롭지 못하게 된다.\n따라서, 사내 데이터에 대한 보안 이슈의 해결을 위해서라도 자체적으로 구축한 모델을 활용해야한다는 숙제를 가지게 된 점도 이번\nPoC의 동기가 되었다.\n3. 왜 Llama3인가? # LLM Agent의 engine으로 Llama3를 선택한 이유는,\n1) 가장 최근에 Llama3가 공개되었기도 했고,\n2) 한국어 fine-tuning모델도 있\u001d고\n3) 유저들의 reference도 가장 많고\n4) 성능의 우수성 때문에 Llama3를 활용하는게 가장 적합한 대안이라 판단했다.\n그리고, 실제 Agent를 동작하게 하려면, 프롬프트를 이해하는 뛰어난 intelligence를 가진 모델이 필수적이다.\n따라서, 최소 조건으로 Llama3 70B 정도 규모의 모델이 필요하다고 판단하였고, 한국어 fine-tuning된 모델인\n\u0026ldquo;Bllossom/llama-3-Korean-Bllossom-70B-gguf-Q4_K_M\u0026quot;을 활용하기로 했다.\n자, 이제 모델도 선택했으니, 본격적으로 Llama3와 Langchain을 통해 Function Call을 구현해보자.\n3. Llama-cpp를 활용한 Llama3의 langchain 연동 # 1) 서버에 Llama3 model을 올린 후, FastAPI를 통해 서빙하는 방식 # -\u0026gt; 이 부분은 개인적인 시행착오를 기록한 부분으로,\n결론만 얻고자하신다면 아래의***\u0026ldquo;2) Llama-cpp를 활용한 Langchain 연동\u0026rdquo;*** 부분으로 넘어가시면 될 것 같다.\n처음에 시도했던 방법은,\nLlama3 모델을 Langchain에 연동하기 위해 FastAPI를 기반으로 서버에 Llama3 70B를 실행한 후,\nChatOpenAI 클래스를 이용해 서버의 모델을 Load했다.\nTools와 parser, model을 AgentExecutor로 \bwrapping하여 내부 프로세스를 통해 적절한 Tool과 arguments를 추출한 후 tool의 결과값을 return하는 방식으로 구현을 시도했다.\n간단히 그림으로 표현하자면, 아래와 같이 동작할것으로 기대하고 구현을 진행했다.\n다만, 이 부분에서 무수히 많은 에러가 발생했는데,\n가장 큰 문제로, 모델이 클라이언트단에서 들어온 input에 대한 답변은 정상적으로 생성해냈지만, 이후 langchain의 agent executor로 model output을 전달할때, 계속 파싱에러가 발생했다.\n즉, 모델은 아래와 같이, input된 질문에 대해 적절한 Tool과 argument를 추출해냈으나,\n해당 데이터가 AgentExecutor 내부에서 다음단계로 넘어가지 못하고 파싱에러를 계속 발생시키는 것이다.\n\u0026lt;모델의 출력결과\u0026gt;\n{ \u0026ldquo;action\u0026rdquo;: \u0026ldquo;korea_city_weather_info\u0026rdquo;, \u0026ldquo;action_input\u0026rdquo;: { \u0026ldquo;city_name\u0026rdquo;: \u0026ldquo;서울\u0026rdquo;\n}\nopenAI의 API(GPT3.5 또는 4, 4o)를 모델로 사용했을때는 문제가 없는것으로 보아, Llama가 뱉는 output의 form을 openAI의 API(Creat chat completion)형태와 동일하게끔 데이터를 일치시키면 되지않을까 판단했고, 동일한 구조로 return하도록 서버코드를 조정하였으나, 동일한 구조에서도 계속해서 파싱에러가 발생했다.\n이 부분에서 가장 많은 시간을 사용했는데, 결론적으로는 langchain의 구조자체가 GPT API가 아닌 오픈소스 모델에 대해서는\n최적화되어있지 않다는 느낌을 받았다\u0026hellip;\n시도했던 서버와 클라이언트단 코드의 일부를 첨부하면 아래와 같다.\n# Server Side model_name = \u0026#34;Bllossom/llama-3-Korean-Bllossom-70B\u0026#34; tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForCausalLM.from_pretrained( model_name, torch_dtype=torch.float16, device_map=\u0026#39;auto\u0026#39;, ) model.eval() class Message(BaseModel): role: str content: str class CompletionUsage(BaseModel): prompt_tokens: int completion_tokens: int total_tokens: int class FunctionTool(BaseModel): name: str description: str parameters: dict class ChoiceMessage(BaseModel): role: str content: str class Choice(BaseModel): index: int message: ChoiceMessage finish_reason: str class AICompletionMessage(BaseModel): id: str object: str created: int model: str choices: list tools: list tool_choice: str usage: CompletionUsage class QueryRequest(BaseModel): model: str messages: List[Message] temperature: Optional[float] = .0 max_tokens: Optional[int] = 8192 top_p: Optional[float] = .9 stop: Optional[List[str]] = [\u0026#34;Observation\u0026#34;, \u0026#34;\\nObservation\u0026#34;, \u0026#34;\\n관측\u0026#34;] @app.post(\u0026#34;/v1/completions/chat/completions\u0026#34;) async def completions(request: QueryRequest): messages = request.messages try: system_prompt = messages[0].content user_input = messages[1].content except IndexError as e: system_prompt = \u0026#39;\u0026#39; user_input = messages[0].content input_ids = tokenizer.apply_chat_template( # formatted_messages, messages, add_generation_prompt=True, return_tensors=\u0026#34;pt\u0026#34; ).to(model.device) terminators = [ tokenizer.eos_token_id, tokenizer.convert_tokens_to_ids(\u0026#34;\u0026lt;|eot_id|\u0026gt;\u0026#34;) ] outputs = model.generate( input_ids, max_new_tokens=8192, eos_token_id=terminators, do_sample=True, temperature=request.temperature, top_p=0.9 ) response_output = tokenizer.decode(outputs[0][input_ids.shape[-1]:], skip_special_tokens=True) response_text = parse_model_output(response_output) prompt_tokens = input_ids.shape[-1] completion_tokens = outputs.shape[-1] - input_ids.shape[-1] if isinstance(response_text, dict): if \u0026#39;action\u0026#39; in response_text and response_text[\u0026#39;action\u0026#39;] == \u0026#39;Final Answer\u0026#39;: finish_reason = \u0026#39;stop\u0026#39; else: finish_reason = \u0026#39;tool_calls\u0026#39; else: finish_reason = \u0026#39;stop\u0026#39; response = creat_AImessage(response_text, finish_reason, prompt_tokens, completion_tokens) return response # Client Side from langchain_openai import ChatOpenAI local_llm = ChatOpenAI( base_url=\u0026#34;http://localhost:8002/v1/completions\u0026#34;, api_key=\u0026#34;llama3_api\u0026#34;, model=\u0026#34;Bllossom/llama-3-Korean-Bllossom-70B\u0026#34;, ) @tool def search_phonenumber(query: str) -\u0026gt; str: \u0026#34;\u0026#34;\u0026#34;장소에 대한 전화번호 검색 결과를 반환할 때 사용되는 도구입니다. 전화번호를 알고싶을때 사용하는 도구입니다.\u0026#34;\u0026#34;\u0026#34; return \u0026#34;삼성전자 본사 전화번호: 010-1234-5678\\n\\n서울지점 OOO 전화번호: 02-123-4567\u0026#34; tools = [search_phonenumber] chat_model_with_stop = local_llm.bind( stop=[\u0026#34;Observation\u0026#34;, \u0026#34;\\nObservation\u0026#34;, \u0026#34;\\n관측\u0026#34;]) json_prompt = hub.pull(\u0026#34;hwchase17/react-chat-json\u0026#34;) llama3_agent = create_json_chat_agent(local_llm, tools, json_prompt) # 로깅 설정 logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger(__name__) # 로깅 콜백 추가 class LoggingCallbackHandler(BaseCallbackHandler): def on_agent_start(self, agent, **kwargs): logger.debug(f\u0026#34;Agent started: {agent}\u0026#34;) def on_agent_step(self, agent, step, **kwargs): logger.debug(f\u0026#34;Agent step: {step}\u0026#34;) def on_agent_finish(self, agent, **kwargs): logger.debug(f\u0026#34;Agent finished: {agent}\u0026#34;) def on_tool_start(self, tool, **kwargs): logger.debug(f\u0026#34;Tool started: {tool}\u0026#34;) def on_tool_finish(self, tool, result, **kwargs): logger.debug(f\u0026#34;Tool finished: {tool} with result: {result}\u0026#34;) # 로깅 콜백 핸들러 인스턴스 생성 logging_callback_handler = LoggingCallbackHandler() # 로깅 콜백 매니저 인스턴스 생성 logging_callback_manager = CallbackManager(handlers=[logging_callback_handler]) llama3_agent_executor = AgentExecutor( agent=llama3_agent, tools=tools, verbose=True, handle_parsing_errors=True, return_intermediate_steps=True, early_stopping_method=\u0026#39;force\u0026#39;, callback_manager=logging_callback_manager ) response = llama3_agent_executor.invoke( {\u0026#34;input\u0026#34;: \u0026#34;삼성전자의 전화번호를 검색하여 결과를 알려주세요.\u0026#34;} ) print(f\u0026#39;답변: {response[\u0026#34;output\u0026#34;]}\u0026#39;) (OpenAI API document : https://platform.openai.com/docs/api-reference/chat/create)\n이 문제를 해결하기 위해, 모델이 로드된 서버와 langchain을 apply하는 부분에 대한 레퍼런스를 찾아보려했지만,\n아무리 찾아봐도 없었고, 결국 다른 방법을 찾기로 했다.\n만약 이 부분에 대한 해결책을 알고 계신분이 있다면 댓글 꼭 부탁드린다\u0026hellip;(꼭..꼭..)\n2) Llama-cpp를 활용한 Langchain 연결 # 다른 방법을 찾던 중, Llama-cpp라는 라이브러리를 알게되었는데, CPU환경에서 sLLM모델을 구동할 수 있도록 내부 구조를\nC++로 개선한 라이브러리였다.\nLlama-cpp의 경우, 개발 목적 자체가 Langchain과의 연동을 목적으로 만들어진 라이브러리는 아니지만,\n최근 GPU 가속이 가능하게 되었고, 무엇보다, Langchain 연동이 가능하도록 구현된 라이브러리였기때문에\n테스트해보기로했다.\n다만, Llama-cpp의 경우 상당히 최신 버전의 python, cuda, torch버전을 요구하고있기때문에,\n라이브러리 설치 이전에 꼭 환경부터 확인하자.\n\u0026lsquo;Llama-cpp\u0026rsquo; Requirements # Requirement\npython version : 3.10 | 3.11 | 3.12 cuda 12.1 | 12.2 | 12.3 | 12.4 * 참고\n환경셋팅할때, 나와 같이 별도의 docker container에서 위 종속성을 셋팅하려한다면,\n처음부터 최신 버전의 cuda환경을 가지고 있다면 문제되지않지만,\nhost server의 cuda버전이 구형이라, nvidia 12.1 toolkit을 호환하지않을 경우,\ncuda12.1이 내장된 이미지로 바로 container를 생성할 수 없는데(host의 nvidia driver에 걸려서 container 생성이 불가하다) 이때는 ubuntu만 설치된 image로 container를 생성한후에 container 내부에서 cuda, python을 설치하는것을 추천한다\u0026hellip;\n(*docker container는 host server와 다른 버전의 cuda환경을 구축할 수 있지만, host가 너무 낮은 버전을 가지고 있을 경우에는 container에서도 일정 버전이상을 올릴 수 없다. 호환불가.)\n내가 사용중인 서버는 host server의 cuda 버전을 변경해도 딱히, 영향이 갈만한 서비스나 프로젝트가 없었기 때문에,\nhost server의 cuda 및 nvidia-toolkit을 12.5버전을 끌어올린 후 동일버전의 container에서 작업을 진행했다.\n자 그럼, 다시 돌아와서 Llama-cpp를 실행해보도록 해보자.\n우선, Llama-cpp의 공식 문서를 보면, Langchain의 기존 class들과 **\u0026lsquo;완벽 연동\u0026rsquo;**이 가능하다고 기재되어있다.\n이 말에 기쁜 마음으로 llama-cpp 적용을 시도했는데, 결과를 먼저 공유하자면,\n아직까지는 \u0026lsquo;일부 연동\u0026rsquo;으로 표현하는게 맞지않나 생각한다.\n먼저, llama-cpp는 \u0026lsquo;GGUF\u0026rsquo;형식의 모델만 사용이 가능하다.\nGGUF?\nGGUF는 GGML을 사용하여 대형 모델을 실행하는 프로그램과 모델을 저장하는 파일 형식이다.\n참고로 GGML은 보통 컴퓨터에서도 큰 모델을 빠르게 돌릴 수 있는 ML용 라이브러리인데, GGUF는 모델을 빠르고 쉽게 불러오고 저장할 수 있게 해주는 바이너리 형식으로 설계되었다. 바이너리 형식이란 정보를 0과 1의 조합으로 표현하는 컴퓨터만 읽을 수 있는 형식을 말한다. 개발자들은 보통 PyTorch 같은 프로그래밍 도구를 사용해 모델을 만든 후, GGML에서 쓸 수 있도록 GGUF 형식으로 저장한다. GGUF는 이전에 사용되던 GGML, GGMF, GGJT와 같은 형식을 개선하여 모든 필요한 정보를 담고 있으며, 새로운 정보를 추가해도 기존 모델과 잘 맞도록 확장성을 가지고 설계되었다고 한다. * Ref - https://wooiljeong.github.io/ml/gguf-llm/#google_vignette\n실행방법은 매우 간단한데, LlamaCpp 클래스를 이용해, 다운받은 모델경로를 지정해주고 관련 옵션을 정의한 후 불러올 수 있다.\nn_gpu_layers = -1 n_batch = 512 callback_manager = CallbackManager([StreamingStdOutCallbackHandler()]) llm = LlamaCpp( model_path = os.path.join(model_path, model_name), n_gpu_layers = n_gpu_layers, n_batch = n_batch, n_ctx = 4096, temperature=.0, max_tokens=8192, top_p=.9, callback_manager=callback_manager, verbose=True ) template = \u0026#34;\u0026#34;\u0026#34;Question: {question} Answer: 질문에 대한 답변을 이유를 함께 상세하게 설명해줘.\u0026#34;\u0026#34;\u0026#34; prompt = PromptTemplate.from_template(template) llm_chain = prompt | llm question = \u0026#34;시베리아 호랑이랑 그리즐리 베어가 싸우면 누가이겨요?\u0026#34; llm_chain.invoke({\u0026#34;question\u0026#34;: question}) [OUTPUT] \u0026#39; 이 두 동물의 싸움에서 누가 이길까? 이 문제에 대한 답은 사실 굉장히 간단합니다. 이 두 동물의 싸움에서 누가 이길까? 그 대답은 호랑이입니다. 왜 그럴까요? 다음은 그 이유를 설명합니다. 첫 번째 이유는 호랑이가 그리즐리보다 훨씬 더 강력하다는 것입니다. 호랑이는 고양이과 가장 큰 동물 중 하나이며, 근육질이고 힘센 발톱을 가지고 있습니다. 반면에 그리즐리는 7~8% 정도의 살을 가지고 있으며, 몸집도 호랑이에 비해 작습니다. 또한 호랑이는 뛰어난 전투력과 생존력을 가지고 있습니다. 호랑이는 자신의 영역 안에서 어떤 위험과도 쉽게 대처할 수 있는 능력을 가지고 있습니다. 따라서 호랑이가 그리즐리보다 더 강력하다는 것은 명확하게 증명된 사실입니다. 두 번째 이유는 호랑이와 그리즐리가 서로 다른 환경에서 살아가고 있기 때문에, 호랑이가 더 유리한 환경이 가지고 있다는 것입니다. 호랑이는 대부분 열대 우림이나 사바나 등 따뜻한 기후와 풍부한 먹이 자원을 가진 지역에서 살고 있습니다. 반면에 그리즐리는 주로 북미 서부 지역에 분포하고 있으며, 이 지역에서는 상대적으로 추운 기후와 제한된 먹이자원 등으로 인해 생존하기 어려워지는 경우가 많습니다. 따라서 호랑이가 더 유리한 환경에서 살아가고 있기 때문에, 호랑이가 그리즐리보다 이길 확률이 높다고 할 수 있습니다. 결론적으로 호랑이와 그리즐리가 싸우는 경우, 호랑이가 더 강력하고 유리한 환경에서 살아있으므로, 호랑이가 이길 가능성이 높다고 할 수 있습니다.\u0026#39; [output]이 출력될때, LlamaCpp클래스 내부에 streaming = True가 default기때문에,\n바로 스트리밍 적용되어서 출력되는것을 확인할 수 있다.\n그리고, 위에서 보다시피, langchain에서 사용하는 Runnable 프로토콜 방식 \u0026lsquo;|\u0026rsquo; 을 사용해서 각 스텝을 묶을 수 있다.\n그리고, langchain과 동일하게 invoke를 통해 query에 대한 답변을 출력할 수 있다.\n위 내용을 봤을때 Langchain의 클래스들과 잘 호환이 되어보였고,\n위의 FastAPI를 구현했던 코드에서 모델 부분만 Llama-Cpp를 통해 Load한 모델로 교체해서 실행했다.\n# 검색 결과를 요청 후 질문에 대한 답변을 출력합니다. response = agent_executor.invoke( { \u0026#34;input\u0026#34;: \u0026#34;서울의 날씨를 알려줘\u0026#34;, \u0026#34;chat_history\u0026#34;: [], } ) print(f\u0026#39;답변: {response[\u0026#34;output\u0026#34;]}\u0026#39;) [OUTPUT] \u0026gt; Entering new AgentExecutor chain... korea Llama.generate: prefix-match hit _city_weather_info(서울) ```python { \u0026#34;action\u0026#34;: \u0026#34;korea_city_weather_info\u0026#34;, \u0026#34;action_input\u0026#34;: { \u0026#34;city_name\u0026#34;: \u0026#34;서울\u0026#34; } } korea_city_weather_info(서울) 서울 날씨: 흐림, 11도 llama_print_timings: load time = 712.64 ms llama_print_timings: sample time = 109.95 ms / 86 runs ( 1.28 ms per token, 782.19 tokens per second) llama_print_timings: prompt eval time = 0.00 ms / 0 tokens ( -nan ms per token, -nan tokens per second) llama_print_timings: eval time = 4358.14 ms / 86 runs ( 50.68 ms per token, 19.73 tokens per second) llama_print_timings: total time = 4626.04 ms / 86 tokens Could not parse LLM output: korea_city_weather_info(서울)\n{ \u0026#34;action\u0026#34;: \u0026#34;korea_city_weather_info\u0026#34;, \u0026#34;action_input\u0026#34;: { \u0026#34;city_name\u0026#34;: \u0026#34;서울\u0026#34; } } korea_city_weather_info(서울) 서울 날씨: 흐림, 11도 ```Invalid or incomplete response \u0026gt; Finished chain. 답변: Agent stopped due to iteration limit or time limit. 결과는 실패였다.\n원인을 분석해보자면, llama가 뱉은 action 및 action input의 형태가 \u0026ldquo;```python\u0026rdquo; 으로 시작하도록 되어있는데,\nLangchain 내부에서 모델의 출력값을 parsing처리하기 위해서는 \u0026ldquo;Action:```\u0026rdquo; 로 시작해야지만, 내부 parser를 탈 수 있게된다.\n이때문에 parser를 타지못하고, limit iteration인 16을 넘어갈 경우(limit 변경 가능), 프로세스를 비정상 종료하게 된 것 이다.\n다만, 출력값의 형태문제를 해결하기 위해, 프롬프트를 개선해봤지만, 여전히 모델의 출력값이 일정하지 않은 모습을 보였고,\n출력데이터를 parser에 바로 적용하기 어려웠다.\n결론 # 결과적으로 오픈소스 sLLM, Llama를 이용해서 langchain의 function call agent를 동작할 수 있게 하려면,\n위와 같이 바로 apply하는 방식으로 쉽지않다고 판단했다.\n다만, Reddit에 보니, Function call에 적합한 데이터로 fine-tuning을 할 경우, Llama2에서도 적용이 가능하다는\n내용을 확인했고, 추후 현재의 Llama3 70B model을 function call에 적합하도록 fine-tuning후에 다시 시도해보려한다.\n대안 # Llama3를 통해 Langchain Agent를 실행하는 것은 실패했지만,\nLangchain cycle을 타는것은 어렵지만, 1회 동작으로 Tool을 선택하고, 실행하는 것은 가능하지 않을까 생각했다.\n즉, Agent처럼, 정답을 찾아낼때까지 반복적으로 적절한 Tool을 찾고, 답변을 얻는 방식은 어렵지만,\n1회에 한하여, 적절한 Tool을 선택하고 답변을 구할 수는 있지않을까 생각했다.\nLanggraph라이브러리에는 특정 형태의 실행 데이터를 통해 Tool(function)을 실행할 수 있는 Tool Executor 클래스를 제공하고있다.\n따라서, Tool Executor가 Tool을 실행할 수 있는 형태로만 데이터를 잘 넣어주면 함수 실행결과를 Return 받을 수 있을 것이다.\n실제 테스트를 위해 몇가지 Tool을 생성한후에 위 내용을 간단히 구현해보았다.\n먼저 Llama model을 정의하고,\nfrom llama_cpp import Llama from transformers import AutoTokenizer model_id = \u0026#39;Bllossom/llama-3-Korean-Bllossom-70B-gguf-Q4_K_M\u0026#39; tokenizer = AutoTokenizer.from_pretrained(model_id) model = Llama( model_path=\u0026#39;/workspace/wontae_kim/llama-3-Korean-Bllossom-70B-gguf-Q4_K_M.gguf\u0026#39;, n_ctx=1024, n_gpu_layers=-1 # Number of model layers to offload to GPU ) Query에 적합한 Tool과 argument를 추출할 수 있는지 확인하기 위해 몇가지 더미 도구를 정의하였다.\n@tool def korea_city_weather_info(city_name: str) -\u0026gt; str: \u0026#34;\u0026#34;\u0026#34; Tool Description : The weather information of the city inputted by the user is outputted. Args : \u0026#34;city_name\u0026#34; : Name of the city to look up the weather \u0026#34;\u0026#34;\u0026#34; return \u0026#34;오늘 서울의 온도는 최고 24.2도, 최저 13.5도를 보이며, 낮 한때 소나기가 있겠습니다.\u0026#34; @tool def search_phonenumber(query: str) -\u0026gt; str: \u0026#34;\u0026#34;\u0026#34;장소에 대한 전화번호 검색 결과를 반환할 때 사용되는 도구입니다. 전화번호를 알고싶을때 사용하는 도구입니다.\u0026#34;\u0026#34;\u0026#34; return \u0026#34;판교 몽중헌 전화번호: 010-1234-5678\\n\\n서울 OOO 전화번호: 02-123-4567\u0026#34; @tool def get_date(): \u0026#34;\u0026#34;\u0026#34;오늘의 날짜를 알고싶을때, 사용하는 도구입니다.\u0026#34;\u0026#34;\u0026#34; from datetime import datetime date = datetime.now() return f\u0026#34;오늘은 {date.year}년{date.month}월{date.day}일 입니다.\u0026#34; @tool def get_team_member(team_name:str): \u0026#34;\u0026#34;\u0026#34; 팀의 구성원 정보를 조회하는 도구입니다. \u0026lt;Argument\u0026gt; - team_name : 구성원을 조회할 팀명을 입력합니다. \u0026lt;질문예시\u0026gt; - 데이터시각화팀 인원보여줘 - 설계팀 구성원 보여줘 - 기획4팀 팀원이 누가있지? \u0026#34;\u0026#34;\u0026#34; return \u0026#39;박태환, 루피, 안정환, 카리나, 윈터, 조로, 유재석 총 7명입니다. \u0026#39; @tool def animal_habitat_search(animal_name:str): \u0026#34;\u0026#34;\u0026#34; 동물의 주요 서식지 정보를 알고싶을때 사용하는 도구입니다. \u0026#34;\u0026#34;\u0026#34; return f\u0026#39;{animal_name}의 주요서식지 : 아마존 강남구 31번지\u0026#39; @tool def working_time_search(employee_nm:str): \u0026#34;\u0026#34;\u0026#34; 조회한 직원의 근무시간을 알고싶을때 사용하는 도구입니다. \u0026lt;Argument\u0026gt; - employee_nm : 근무시간을 조회할 직원의 이름을 입력합니다. \u0026#34;\u0026#34;\u0026#34; return f\u0026#39;{employee_nm}의 {datetime.now().year}년 {datetime.now().month}월 총 근무시간 : 999시간\u0026#39; tools = [korea_city_weather_info, search_phonenumber, get_date, get_team_member, animal_habitat_search, working_time_search] 그리고, 테스트를 통해 적절한 시스템 프롬프트를 정의하였고,\n마지막으로 ReActJsonSingleInputOutputParser를 통과한 데이터를 열어서\ntool name과 argument를 정제하는 코드를 일부 삽입하였고, 이를 통과한 context가, Tool Executor를 통해 도구를 실행하게끔 했다.\n(이 부분이 fine-tuning으로 해결할 수 있는 부분이 아닐까 한다\u0026hellip;)\ndef tool_arg_generator(user_input:str, tools): print(f\u0026#39;\\n--\u0026gt; User Question : {user_input}\\n\\n\u0026#39;) tools_info = render_text_description_and_args(tools).replace(\u0026#39;{\u0026#39;, \u0026#39;{{\u0026#39;).replace(\u0026#39;}\u0026#39;, \u0026#39;}}\u0026#39;) tools_name = [t.name for t in tools] query = f\u0026#34;{user_input} --\u0026gt; 라는 질문과 관련된 tool_name과 Argument에 대해서 system_prompt를 참고한 양식으로 출력해줘\u0026#34; system_prompt = f\u0026#34;\u0026#34;\u0026#34; 당신은 주어진 질문에 적절한 도구(Tool)을 선택하여 제시하는 역할을 합니다.. You are responsible for selecting and presenting the appropriate tool for the given question. * 사용자 질문(Question)에 대해 아래 \u0026#39;instructions\u0026#39;와 \u0026#39;출력예시\u0026#39;을 참고해서 사용할 Tool과 Tool에 input할 argument를 출력해라. \u0026lt;Instructions\u0026gt; - 아래 Tool List의 도구 중, 사용자의 질문과 관련된 도구를 선택하고 도구 이름과 도구에 입력해야할 인수(args)를 아래와 같은 Json형태의 데이터로 출력해라 - 반드시 아래의 Example of output의 양식을 지켜 반드시 Json 형태로 출력하라 - Tool name은 반드시 \u0026#39;Tools Name\u0026#39;에 포함된 이름이어야한다. - \u0026#39;action\u0026#39;과 \u0026#39;action_input\u0026#39;에 포함될 key에는 공백이 절대로 들어갈 수 없다. \u0026lt;Tool List\u0026gt; {tools_info} \u0026lt;Tools Name\u0026gt; {tools_name} \u0026lt;출력예시\u0026gt; {{ \u0026#34;action\u0026#34;: \u0026#39;사용할 도구의 이름\u0026#39; \u0026#34;action_input\u0026#34;: \u0026#39;도구에 input되어야 할 argument를 전달합니다.\u0026#39; }} \u0026lt;출력예시 관련 지시사항\u0026gt; - \u0026#39;action\u0026#39;에 포함되는 도구이름에는 반드시 공백을 제거한다. - \u0026#39;action_input\u0026#39;에 포함되는 dictionary에는 type정보는 표시하지 않는다. - \u0026#39;action_input\u0026#39;에 포함되는 dictionary의 key는 반드시 공백을 제거한다. \u0026#34;\u0026#34;\u0026#34; messages = [ {\u0026#34;role\u0026#34;: \u0026#34;system\u0026#34;, \u0026#34;content\u0026#34;: f\u0026#34;{system_prompt}\u0026#34;}, {\u0026#34;role\u0026#34;: \u0026#34;user\u0026#34;, \u0026#34;content\u0026#34;: f\u0026#34;{query}\u0026#34;} ] prompt = tokenizer.apply_chat_template( messages, tokenize = False, add_generation_prompt=True ) generation_kwargs = { \u0026#34;max_tokens\u0026#34;:1024, \u0026#34;stop\u0026#34;:[\u0026#34;\u0026lt;|eot_id|\u0026gt;\u0026#34;], \u0026#34;echo\u0026#34;:True, # Echo the prompt in the output \u0026#34;top_p\u0026#34;:0.9, \u0026#34;temperature\u0026#34;:0.001, } resonse_msg = model(prompt, **generation_kwargs) get_tool_info = resonse_msg[\u0026#39;choices\u0026#39;][0][\u0026#39;text\u0026#39;][len(prompt):] print(f\u0026#39;************* 모델을 통해 질문에 적합한 Tool 선택 및 Argument생성을 완료하였습니다.*************\u0026#39;) print(f\u0026#39;{get_tool_info}\\n ********************************************\u0026#39;) print(get_tool_info) get_tool_info = get_tool_info.lower() return get_tool_info def tool_executor_parser(tool_description): stop_key = [\u0026#39;Type\u0026#39;, \u0026#39;string\u0026#39;] des =f\u0026#34;\u0026#34;\u0026#34; Action:``` {tool_description} ``` \u0026#34;\u0026#34;\u0026#34; from langchain.agents.output_parsers import ReActJsonSingleInputOutputParser parser = ReActJsonSingleInputOutputParser() action = parser.parse(des) action.tool = action.tool.strip().replace(\u0026#39;-\u0026#39;, \u0026#39;_\u0026#39;).replace(\u0026#39; \u0026#39;, \u0026#39;_\u0026#39;).lower() # tool 이름 정리하자. action.tool_input = {k.strip().replace(\u0026#39;-\u0026#39;, \u0026#39;_\u0026#39;).replace(\u0026#39; \u0026#39;, \u0026#39;_\u0026#39;).lower():v for k, v in action.tool_input.items() if k not in stop_key} # tool 내부의 인수를 정리하자. print(f\u0026#39;\\n\\n1) Tool_Name : {action.tool} \\n2) Tool Input Argument : {action.tool_input}\\n********************************************\u0026#39;) return action def cutom_Tool_Executor(user_input, tools): \u0026#34;\u0026#34;\u0026#34; * tools : 함수에 \u0026#39;@\u0026#39;데코레이터를 붙인 후 리스트에 담은 형태 필요 * action : 내부에 \u0026#39;Action\u0026#39; 키워드로 감싼 후, action, action_input이 포함된 dictionary를 \u0026#39;ReActJsonSingleInputOutputParser\u0026#39;클래스로 파싱처리한 데이터 input필요 \u0026#34;\u0026#34;\u0026#34; tool_info = tool_arg_generator(user_input=user_input, tools=tools) action = tool_executor_parser(tool_info) from langgraph.prebuilt import ToolExecutor tool_executor = ToolExecutor(tools) print(\u0026#39;\\n\\n\\n======================== TOOL RETURN ========================\u0026#39;) return tool_executor.invoke(action) 그리고 결과는 아래와 같이 출력되었다.\nuser_input = \u0026#39;데이터사이언스셀의 팀원정보 보여줘\u0026#39; cutom_Tool_Executor(user_input, tools) [OUTPUT] llama_tokenize_internal: Added a BOS token to the prompt as specified by the model but the prompt also starts with a BOS token. So now the final prompt starts with 2 BOS tokens. Are you sure this is what you want? --\u0026gt; User Question : 데이터사이언스셀의 팀원정보 보여줘 Llama.generate: prefix-match hit llama_print_timings: load time = 2269.42 ms llama_print_timings: sample time = 55.39 ms / 38 runs ( 1.46 ms per token, 686.03 tokens per second) llama_print_timings: prompt eval time = 0.00 ms / 0 tokens ( -nan ms per token, -nan tokens per second) llama_print_timings: eval time = 1956.30 ms / 38 runs ( 51.48 ms per token, 19.42 tokens per second) llama_print_timings: total time = 2063.37 ms / 38 tokens ************* 모델을 통해 질문에 적합한 Tool 선택 및 Argument생성을 완료하였습니다.************* { \u0026#34;action\u0026#34;: \u0026#34;get-team-member\u0026#34;, \u0026#34;action_input\u0026#34;: { \u0026#34;Team Name\u0026#34;: \u0026#34;데이터사이언스셀\u0026#34; } } ******************************************** { \u0026#34;action\u0026#34;: \u0026#34;get-team-member\u0026#34;, \u0026#34;action_input\u0026#34;: { \u0026#34;Team Name\u0026#34;: \u0026#34;데이터사이언스셀\u0026#34; } } 1) Tool_Name : get_team_member 2) Tool Input Argument : {\u0026#39;team_name\u0026#39;: \u0026#39;데이터사이언스셀\u0026#39;} ******************************************** ======================== TOOL RETURN ======================== \u0026#39;박태환, 루피, 안정환, 카리나, 윈터, 조로, 유재석 총 7명입니다. \u0026#39; 위 내용을 보면, 모델이 함수명과 argument를 적절하게 추출하였으나,\naction_input에서 변수명칭을 \u0026quot; get-team-member\u0026quot;으로 잘못 추출한것을 볼 수 있다.\n이것을 customizing한 parser를 통해 바로잡아주어 도구를 잘 탔고, 출력값은 정상적으로 출력된것을 볼 수 있다.\n다만, 모든 예외 케이스를 customizing하여 임의로 조정해줄 수 없기때문에,\n추후에는 결과적으로 function call을 위해 llama모델을 fine-tuning해볼 필요는 분명히 있는 것 같다.\n오늘은 Llama3 70B 모델을 사용해서 Function Call을 구현해보았다.\n사실 Agent를 완벽하게 구현한것은 아니고, 한계점이 보이는 프로젝트였지만,\nfine-tuning을 통해 적절한 형태로 답변을 뱉도록 학습만 된다면,\n추후 성능개선 및 Agent의 적용 가능성은 충분히 볼 수 있었다고 생각한다.\n다음에는 Function call fine-tuning과정과 효과에 대해서도 한번 정리해보도록 하겠다.\n* Reference # [model]\nhttps://huggingface.co/Bllossom/llama-3-Korean-Bllossom-70B-gguf-Q4_K_M\n[llama-cpp]\nLangchain - Llama.cpp : https://python.langchain.com/v0.1/docs/integrations/llms/llamacpp/#gpu\npython binding llama-cpp github(official) : https://github.com/abetlen/llama-cpp-python\nllama-ccp tutorial : https://kubito.dev/posts/llama-cpp-linux-nvidia/\n[Langchain \u0026amp; Function Call]\nYoutube 테디노트 : https://www.youtube.com/@teddynote ","date":"2024.05.28","externalUrl":null,"permalink":"/posts/94/","section":"글","summary":"","title":"Llama3-70B(Llama-cpp)로 Langchain Function Call 구현하기","type":"posts"},{"content":"","date":"2024.05.02","externalUrl":null,"permalink":"/tags/mecab/","section":"Tags","summary":"","title":"Mecab","type":"tags"},{"content":"Mecab 을 사용하는 가장 큰 이유는 빠르면서도 뛰어난 Tokeinzing 성능과 함께 user dictionary를 활용해서\n특정 token의 tokenizing 형태와 우선순위를 조정할 수 있다는 점이다.\n평소에 mecab을 자주 사용하지는 않기때문에, 매번 설치하고 셋팅할때마다, 여러 오류들에 직면하게 되는데\n정상적으로 setting을 완료하기 위한 진행 순서를 정리해보고자 한다.\n먼저, mecab을 활용해 한국어를 tokenizing하기 위해서는 mecab과 mecab-ko-dic을 각각 설치해야한다.\n각각의 역할은 아래와 같다.\n**1) mecab(ex. mecab-0.996-ko-0.9.2) :**해당파일은 MeCab의 핵심 엔진을 포함하고 있습니다. 이 엔진은 텍스트를 형태소 분석하는 데 사용되며, 다양한 언어의 사전을 사용할 수 있도록 설계되어 있다.\n2) mecab-ko-dic(ex. mecab-ko-dic-2.1.1-20180720) :\n이 패키지는 한국어를 위한 사전 데이터로, MeCab 엔진을 사용하여 한국어 텍스트를 분석할 때 필요한 사전 파일들을 포함하고 있다.\n자, 이제 차례대로 설치 과정을 살펴보자.\n1. Mecab 엔진 설치하기 # wget https://bitbucket.org/eunjeon/mecab-ko/downloads/mecab-0.996-ko-0.9.2.tar.gz tar xvfz mecab-0.996-ko-0.9.2.tar.gz cd mecab-0.996-ko-0.9.2 # 지금부터 아래 과정들은 가급적 sudo권한으로 실행한다. ./configure # -\u0026gt; 이 명령은 설치할 소프트웨어의 구성을 준비하는 단계이다. # configure 스크립트는 시스템 환경을 검사하여 컴파일러 옵션, 라이브러리 위치, 기타 의존성을 확인하고, # 이 정보를 바탕으로 Makefile을 생성합니다. # Makefile은 실제 빌드 프로세스(make 명령을 사용할 때)를 지시하는 파일입니다. # 사용자는 필요에 따라 ./configure 명령에 다양한 옵션을 지정할 수 있어, # 소프트웨어가 특정 조건에 맞게 설치될 수 있도록 한다. sudo make # make 명령은 Makefile에 정의된 지시에 따라 소프트웨어를 컴파일하고 빌드하는 과정을 수행한다. # 이 단계에서 소스 코드가 실행 가능한 바이너리 파일로 컴파일된다. # make 과정은 프로젝트에 포함된 모든 필요한 컴포넌트를 컴파일하여 준비한다. sudo make check # 이 명령은 빌드된 소프트웨어의 테스트를 실행한다 # make check는 make를 통해 생성된 실행 파일이 예상대로 작동하는지 검증하기 위한 자체 테스트 케이스를 실행한다. # 이 과정을 통해 설치 전에 소프트웨어의 기능성을 확인할 수 있다 sudo make install # make install 명령은 make로 빌드된 파일들을 시스템의 표준 위치에 복사한다. # 이는 일반적으로 /usr/local/bin과 같은 디렉토리에 실행 파일을 배치하고, # 라이브러리 파일은 /usr/local/lib에 배치하는 등의 작업을 포함한다. # 이렇게 함으로써, 시스템 전체에서 해당 프로그램을 사용할 수 있게 된다. sudo ldconfig # 시스템이 새로 설치된 라이브러리(예: /usr/local/lib에 설치된 라이브러리)를 찾을 수 있도록 # 라이브러리 캐시를 업데이트한다. # 이는 새로 설치된 라이브러리 파일을 시스템이 제대로 인식하고 사용할 수 있도록 한다. mecab --version # mecab이 제대로 설치되고, 인식되는지 확인 1-1) 오류 발생 시 # 만약 위 순서대로 실행했는데 진행이 안될 경우 아래 순서대로 다시 진행해본다.\n1) 먼저 libmecab.so.2 파일이 /usr/local/lib에 실제로 존재하는지 확인한다.\nls /usr/local/lib | grep libmecab 라이브러리 파일(libmecab.so.2)이 존재한다면, 환경 변수 LD_LIBRARY_PATH에 해당 경로를 추가하여 라이브러리를 찾을 수 있도록 한다. 이렇게 하려면, 다음 명령을 사용하여 현재 세션에 대해 환경 변수를 설정하거나, .bashrc 파일에 추가할 수 있게된다. export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH echo \u0026#39;export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH\u0026#39; \u0026gt;\u0026gt; ~/.bashrc source ~/.bashrc 시스템이 라이브러리 변경사항을 인식할 수 있도록 ldconfig 명령을 사용한다. 이 명령은 관리자 권한이 필요하다 sudo ldconfig mecab을 인식하는지 확인한다. mecab --version 2) Mecab-ko-dic 설치하기 # wget https://bitbucket.org/eunjeon/mecab-ko-dic/downloads/mecab-ko-dic-2.1.1-20180720.tar.gz tar xvfz mecab-ko-dic-2.1.1-20180720.tar.gz cd mecab-ko-dic-2.1.1-20180720 # 아래 과정은 위와 동일하므로 굳이 설명하지 않겠다. sudo ./configure sudo make sudo make install # 2-1) 만약 설치 후 오류가 발생할 경우 # # mecab 파이썬을 설치해본다 pip3 install --index-url https://pypi.org/simple/ mecab-python3 3) User-dict 등록하기(custumizing) # # 1. mecab-ko-dic 디렉토리 내부에서 사전을 연다(nnp.csv) nnp_path = \u0026#39;/home/jovyan/WIDE_HOME/wontae_kim/mecab-ko-dic-2.1.1-20180720/NNP.csv\u0026#39; nnp = pd.read_csv(nnp_path, header=None, sep=\u0026#39;,\u0026#39;) # 2. 사전 내부에 tokenizer를 custumizing할 token들을 넣고 저장한다.(아래는 예시) nnp = nnp.iloc[:2370] #추가할때마다 중복 추가 저장될 수 있어서, 기존 원본범위에서부터 업데이트하도록 함 new_lines = [ [\u0026#39;주유비\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;NNP\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;F\u0026#39;, \u0026#39;주유비\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;], [\u0026#39;워크샵\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;NNP\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;T\u0026#39;, \u0026#39;워크샵\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;], [\u0026#39;더존\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;NNP\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;T\u0026#39;, \u0026#39;더존\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;], [\u0026#39;우아한형제들\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;NNP\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;T\u0026#39;, \u0026#39;우아한형제들\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;], [\u0026#39;지에스칼텍스\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;NNP\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;T\u0026#39;, \u0026#39;지에스칼텍스\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;], [\u0026#39;대중교통비\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;NNP\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;T\u0026#39;, \u0026#39;대중교통비\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;], [\u0026#39;택시비\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;NNP\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;T\u0026#39;, \u0026#39;택시비\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;], [\u0026#39;커피\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;NNP\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;F\u0026#39;, \u0026#39;커피\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;], [\u0026#39;파리바게트\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;NNP\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;F\u0026#39;, \u0026#39;파리바게트\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;], [\u0026#39;교통비\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;NNP\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;T\u0026#39;, \u0026#39;교통비\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;], [\u0026#39;대중교통\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;NNP\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;T\u0026#39;, \u0026#39;대중교통\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;], [\u0026#39;쏘카\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;NNP\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;F\u0026#39;, \u0026#39;쏘카\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;], [\u0026#39;야놀자\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;NNP\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;T\u0026#39;, \u0026#39;야놀자\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;], [\u0026#39;네이버페이\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;NNP\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;F\u0026#39;, \u0026#39;네이버페이\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;], [\u0026#39;카카오페이\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;NNP\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;F\u0026#39;, \u0026#39;카카오페이\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;], [\u0026#39;쿠팡페이\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;NNP\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;F\u0026#39;, \u0026#39;쿠팡페이\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;], [\u0026#39;여기어때\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;NNP\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;F\u0026#39;, \u0026#39;여기어때\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;], [\u0026#39;호텔스컴바인\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;NNP\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;T\u0026#39;, \u0026#39;호텔스컴바인\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;], [\u0026#39;아고다\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;NNP\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;F\u0026#39;, \u0026#39;아고다\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;], [\u0026#39;에어비엔비\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;NNP\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;F\u0026#39;, \u0026#39;에어비엔비\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;], [\u0026#39;GS25\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;NNP\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;F\u0026#39;, \u0026#39;GS25\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;], [\u0026#39;분식\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;NNP\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;T\u0026#39;, \u0026#39;분식\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;], ] dict_make_cols = [\u0026#39;경비용도명\u0026#39;, \u0026#39;가맹점업종명\u0026#39;, \u0026#39;경비회계계정명\u0026#39;] # 해당 컬럼의 데이터들은 반복적으로 사용되는 명칭이므로, tokenizing진행 시, 쪼개지지않도록 사전에 등록하여 관리 for col in dict_make_cols: for value in df[col].unique(): temp_tokens = re.split(r\u0026#39;[\\s\\-_,.(){}\\[\\]\\/]+\u0026#39;, value) #특수문자 있을 경우, 특수문자 제거 및 특수문자 기준으로 텍스트 분할 temp_tokens = [filter_korean(i) for i in temp_tokens] temp_tokens = [token for token in temp_tokens if token] #공백문자 있을 경우 제거 temp_tokens = [i for i in temp_tokens if len(i)\u0026gt;1] # 길이가 2이상인 token만 남김 # temp_tokens = [token for token in temp_tokens if not any(stop_word in token for stop_word in stop_words)] #stop_words 제거 for token in temp_tokens: jongseong_yn = has_jongseong_token(token) temp_line = [token, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;NNP\u0026#39;, \u0026#39;*\u0026#39;, jongseong_yn, token, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;, \u0026#39;*\u0026#39;] new_lines.append(temp_line) for line in new_lines: nnp.loc[len(nnp)]= line nnp.to_csv(nnp_path, index=False, header=False, sep=\u0026#39;,\u0026#39;) # 추가된 사전 저장 # 3. 파일을 저장한다. sudo tools/add-userdic.sh # 이 명령은 MeCab 설치 폴더 내에 있는 tools 디렉토리에 포함된 add-userdic.sh 스크립트를 # 실행하는 역할을 하며 이 스크립트의 주된 목적은 사용자 정의 사전을 MeCab에 추가하는 것이다 sudo make install # make install 명령은 앞서 make 명령을 통해 컴파일된 소프트웨어를 시스템의 표준 위치에 설치한다. # 해당 과정은 일반적으로 컴파일된 실행 파일, 라이브러리 파일, 설정 파일 등을 # /usr/local/bin, /usr/local/lib, /usr/local/etc 등의 디렉토리에 복사하는 역할을 한다. # 3-1) 사전 추가 시 오류 발생할 경우 # 여기서 오류가 발생하는 일은 잘없다. 만약 오류가 발생하면, 해당 dicpath의 읽기권한이 없어서일수있으니\n현재 디렉토리에 대한 편집권한을 부여해보자\nsudo chmod -R a+r /home/jovyan/WIDE_HOME/wontae_kim/mecab-ko-dic-2.1.1-20180720 # * 추가 : 사용자 사전 내부의 형식은 어떤 의미를 표현하는가? # ref. https://tape22.tistory.com/6\n\u0026lsquo;주유비\u0026rsquo;, \u0026lsquo;*\u0026rsquo;, \u0026lsquo;*\u0026rsquo;, \u0026lsquo;*\u0026rsquo;, \u0026lsquo;NNP\u0026rsquo;, \u0026lsquo;*\u0026rsquo;, \u0026lsquo;F\u0026rsquo;, \u0026lsquo;주유비\u0026rsquo;, \u0026lsquo;*\u0026rsquo;, \u0026lsquo;*\u0026rsquo;, \u0026lsquo;*\u0026rsquo;, \u0026lsquo;*\u0026rsquo;\n사전에 위와 같은 row가 있다고 가정하자.\n이때, 각 자리의 의미는 아래와 같다.\n단어, 좌문맥ID, 우문맥ID, 가중치(단어비용), 품사태그, 의미분류, 종성유무, 타입, 첫 품사, 마지막 품사, 표현\n이중 필수적으로 입력해야 하는 것들은 단어, 품사태그, 의미분류, 종성유무, 타입 정도 이다. 품사태그는 위에서 봤던 대로 NNP, NNG, VV 등의 태그를 의미하고\n종성유무는 받침이 있으면 T, 없으면 F로 적어준다.\n품사 태그 설명\nhttps://docs.google.com/spreadsheets/d/1-9blXKjtjeKZqsf4NzHeYJCrr49-nXeRF6D80udfcwY/edit#gid=6\n","date":"2024.05.02","externalUrl":null,"permalink":"/posts/92/","section":"글","summary":"","title":"Mecab 설치 및 trouble shooting 하기(+user-dict 셋팅)","type":"posts"},{"content":"","date":"2024.05.02","externalUrl":null,"permalink":"/tags/token/","section":"Tags","summary":"","title":"Token","type":"tags"},{"content":"","date":"2024.05.02","externalUrl":null,"permalink":"/tags/tokenizer/","section":"Tags","summary":"","title":"Tokenizer","type":"tags"},{"content":"","date":"2024.05.02","externalUrl":null,"permalink":"/tags/tokenizing/","section":"Tags","summary":"","title":"Tokenizing","type":"tags"},{"content":"","date":"2024.05.02","externalUrl":null,"permalink":"/categories/troubleshooting/","section":"Categories","summary":"","title":"Troubleshooting","type":"categories"},{"content":"","date":"2024.05.02","externalUrl":null,"permalink":"/tags/user-dic/","section":"Tags","summary":"","title":"User-Dic","type":"tags"},{"content":"","date":"2024.05.02","externalUrl":null,"permalink":"/tags/user-dict/","section":"Tags","summary":"","title":"User-Dict","type":"tags"},{"content":"","date":"2024.05.02","externalUrl":null,"permalink":"/tags/%ED%98%95%ED%83%9C%EC%86%8C%EB%B6%84%EC%84%9D%EA%B8%B0/","section":"Tags","summary":"","title":"형태소분석기","type":"tags"},{"content":"","date":"2024.02.28","externalUrl":null,"permalink":"/tags/chroma/","section":"Tags","summary":"","title":"Chroma","type":"tags"},{"content":"","date":"2024.02.28","externalUrl":null,"permalink":"/tags/milvus/","section":"Tags","summary":"","title":"Milvus","type":"tags"},{"content":"","date":"2024.02.28","externalUrl":null,"permalink":"/categories/ml--modeling/","section":"Categories","summary":"","title":"ML \u0026 Modeling","type":"categories"},{"content":"","date":"2024.02.28","externalUrl":null,"permalink":"/tags/pinecone/","section":"Tags","summary":"","title":"Pinecone","type":"tags"},{"content":"","date":"2024.02.28","externalUrl":null,"permalink":"/tags/qdrant/","section":"Tags","summary":"","title":"Qdrant","type":"tags"},{"content":"","date":"2024.02.28","externalUrl":null,"permalink":"/tags/vector-database/","section":"Tags","summary":"","title":"Vector Database","type":"tags"},{"content":"","date":"2024.02.28","externalUrl":null,"permalink":"/tags/vector-db/","section":"Tags","summary":"","title":"Vector Db","type":"tags"},{"content":"이전 글에서는 Vector DB가 무엇인지와 Vector DB에서 데이터를 검색하기 위한 방법들에 대해서 알아보았다.\n오늘은 Vector DB의 종류들과 특성, 장단점에 대해 분석해보려 한다.\n3. 어떤 Vector DB를 사용해야할까? # VectorDB를 선택할때는 현재 진행하는 서비스 또는 프로젝트의 상황과 환경에 비추어 적절한 DB를 선택할 수 있어야 한다.\n따라서, Vector DB의 여러 요소들을 살펴, 적합한 DB를 선택해야하는데, 아래 기준들을 면밀히 살펴봐야한다.\n1) 성능과 속도\n: 검색 속도\n대규모 데이터셋에서 검색 속도는 가장 중요한 요소 중 하나이다. 검색 알고리즘의 효율성과 데이터 인덱싱 방법이 검색 속도에 큰 영향을 미치게 된다.\n: 삽입 및 업데이트 속도\n데이터의 삽입과 업데이트 속도 또한 벡터 데이터베이스의 성능을 결정하는데 중요하게 고려해야할 요소에 해당된다.\n2) 확장성\n: 수평 확장성\n데이터베이스가 물리적으로 다른 서버에 데이터를 분산하여 저장하고 처리할 수 있는 능력을 의미하는데,\n데이터의 양이 증가함에 따라 데이터베이스 시스템을 쉽게 확장할 수 있는지를 확인해야한다.\n: 수직 확장성\n하드웨어의 업그레이드를 통해 성능을 향상 시킬 수 있는 능력을 의미한다.\n이는 초기 비용은 적지만 ,장기적으로는 한계가 있을 수 있기 때문에 중요한 고려요소에 해당한다.\n3) 지원하는 검색 알고리즘 및 기능\n: ANN(근사 최근접 이웃 검색)\n대부분의 Vector DB는 ANN 검색을 지원한다.\n다양한 ANN 알고리즘 중에서 특정 데이터베이스가 어떤 알고리즘을 지원하고있는지가 매우 중요하다.\n: 고급 인덱싱 기법\n데이터 인덱싱은 검색 성능에 큰 영향을 미친다. 예를들어, HNSW, IVF, PQ와 같은 인덱싱 기법을 빠짐없이 지원하는지는 매우 중요한 요소가 된다.\n4) 용이성 및 관리 편의성\n: 설치 및 설정\n데이터 베이스의 설치와 설치 과정이 간단하고 문서화가 잘 되어 있는지 확인해야한다.\n: 관리 도구 및 인터페이스\n관리자 및 개발자가 데이터베이스를 쉽게 관리하고 사용할 수 있는 도구와 인터페이스를 지원하는지 확인해야한다.\n5) 지원 및 커뮤니티\n: 공식문서\n충분하고 명확한 공식 문서가 있는지 살펴본다\n: 커뮤니티 및 지원\n\b활발한 커뮤니티 지원과 개발자 지원이 있는지 확인한다.\n커뮤니티의 크기와 활동성은 문제 해결에 도움이 될 수 있고, 지속적인 개선이 가능한지를 판단할 수 있는 잣대가 된다.\n6) 비용\n: 라이선스 비용\n오픈소스인지 상용 라이선스가 필요한지, 그리고 비용이 어떻게 구성되는지를 확인해야한다.\n: 운영비용\n클라우드 기반 서비스를 사용할 경우 발생할 수 있는 운영 비용도 고려해야 한다.\n자, 이제 어떤 요소들을 중점적으로 판단해야하는지 확인했으므로,\n아래 테이블을 통해 각 Vector DB들의 특징\b들을 자세히 살펴보고 현재 진행하려는 프로젝트의 환경과 목적에 부합하는\nVector DB를 선택할 수 있다.\nVector DB 오픈소스 유무 지원언어 지원 검색 알고리즘 특징 Weaviate O Python, GO, Java, JS Custom HNSW - NLU기능 내장을 통해 텍스트 데이터에 대한 시맨틱 검색 강화 - GraphQL API 지원을 통해 간편한 질의 가능 Milvus O Python, GO, C++ ANN, HNSW, ANNOY - 대규모 데이터셋에 강점 - 다양한 인덱싱 알고리즘 지원 - 높은 확장성 및 검색 성능 - K8s 를 통한 클라우드 배포 가능 Vespa O Java, C++ HNSW(graph) - 실시간 인덱싱과 데이터처리에 최적화 - 자동화된 스케일링 - 설정과 관리방법은 다소 복잡 - 대규모 프로젝트에만 초점이 맞춰짐 Vald O Go NGT - K8s 기반의 자동화된 운영 및 확장성 - 설정과 운영 복잡 Chroma O Python, JS HNSW - 사용법 간단 - http방식, 디스크저장방식, 인메모리 방식을 선택할 수 있음 Qdrant O Rust HNSW(graph) - Json기반의 REST API와 함께 직관적인 필터링(조회조건 설정) - 사용 난이도 낮음, 빠른검색 Pinecone X - 복합으로 사용하며, 독점 상태 - 클라우드 기반(비용 발생) - 데이터양에 따른 오토 스케일링 가능 - 간단한 API 사용법 - 사용 난이도 낮음 Elastic\bSearch X - Neural hashing / Hamming distance - 커뮤니티 - 전문 vector DB에 비해 벡터 검색기능의 제한 - 설정 및 관리 복잡 마치며 # 결국, 어떤 Vector DB를 도입해야할지에 대해 고민할때는, 적용에 앞서 Vector DB가 꼭 필요한지와 적합성에 대해서 꼭 고민이 필요하다.\nVector DB가 모든 사례에서 긍정적인 효과를 기대할 수 있는것은 아니며, 아래의 경우에 해당한다면\n도입효과는 반감되거나 오히려 추후에 서비스를 제공함에 있어 리스크로 작용할 수 있다.\n먼저, 다루고자 하는 데이터가 모두 vector data인지 검토해야한다.\n만약 vector data뿐아니라 다양한 정형데이터도 함께 관리되어야하며 이미 사용중인 DB가 있을 경우에는\n위의 전문 Vector DB가 적합하지 않을 수 있고, ANN, NN을 지원해주는 기존의 DB를 활용하는것도 방법이다.\npostgres의 pgvector과 같은 대안들을 고려해볼 수 있는것이다.\n또한, 저장된 데이터의 Embedding의 dimension이 클 경우, 검색 알고리즘의 정확도가 낮아질 수 있기 때문에\nembedding size를 고려하여 다른 방법을 강구하는것이 더 좋은 방향일 수도 있다.\n\b그리고, 데이터베이스에 수록할 데이터가 매우 클 경우에도 데이터를 임베딩하고 인덱싱하는 작업 자체에, 많은 시간과 리소스가 필요하게 되므로, 지속적인 데이터베이스 업데이트와 인덱싱이 이뤄져야할 경우 문제가 될 수 있다.\n\b주어진 쿼리에 대해 데이터베이스 내 데이터들과 유사도를 계산함에 있어서도, batch로 처리할지 realtime으로 처리할지에 대한\n고민 또한 필요하기 때문에 고려할 부분이 많아진다.\n따라서, Vector DB를 적용하기 전에, 먼저 복합적인 문제 요소를 다각도로 분석하여, Vector DB가 꼭 필요한지,\n충분히 적합한지에 대한 검토가 깊게 이루어지는 것이 선행되어야 하겠다.\nReference # https://hotorch.tistory.com/406\nhttps://www.ethanrosenthal.com/2023/04/10/nn-vs-ann/\nhttps://medium.com/thirdai-blog/understanding-the-fundamental-limitations-of-vector-based-retrieval-for-building-llm-powered-48bb7b5a57b3\n","date":"2024.02.28","externalUrl":null,"permalink":"/posts/90/","section":"글","summary":"","title":"Vector DB란 무엇일까? 그리고 어떤 Vector DB를 사용해야할까? (2)","type":"posts"},{"content":"","date":"2024.02.27","externalUrl":null,"permalink":"/tags/ann/","section":"Tags","summary":"","title":"Ann","type":"tags"},{"content":"","date":"2024.02.27","externalUrl":null,"permalink":"/tags/annoy/","section":"Tags","summary":"","title":"Annoy","type":"tags"},{"content":"","date":"2024.02.27","externalUrl":null,"permalink":"/tags/faiss/","section":"Tags","summary":"","title":"Faiss","type":"tags"},{"content":"","date":"2024.02.27","externalUrl":null,"permalink":"/tags/hnsw/","section":"Tags","summary":"","title":"Hnsw","type":"tags"},{"content":"오늘은 VectorDB의 종류와 장단점에 대해서 살펴보려 한다.\n먼저 VectorDB는 다른 DB들과 어떻게 다른지 간단하게 정리하고 vector DB에 대해 자세히 알아보도록 하자.\n1. DataBase의 종류 # DataBase는 크게 \u0026lsquo;관계형 데이터베이스\u0026rsquo;, \u0026lsquo;비관계형 데이터베이스\u0026rsquo;, 그리고 최근에 주목받고 있는 **\u0026lsquo;벡터 데이터베이스\u0026rsquo;**로 분류할 수 있다.\n1) 관계형 데이터베이스(RDBMS)\n관계형 DB는 데이터를 테이블로 구성하여 관리하는 데이터베이스를 의미한다.\nSQL(Structured Query Language)을 사용하여 데이터를 쿼리하고 조작하며, 강력한 데이터 무결성을 가지며,\n복잡한 쿼리 처리 능력, 트랜잭션 관리 등이 가능한 DB형태를 가진다.\n대표적으로는 MySQL, PostgreSQL, Oracle, Microsoft SQL Server 등이있고,\n주로 금융 시스템, 고객 관리 시스템, 온라인 상점 등 데이터 관계가 중요한 애플리케이션과 같은 서비스에 사용된다.\n2) 비관계형 데이터베이스(NoSQL)\n비관계형 DB는 스키마가 없거나 유연한 스키마를 가지며, 대량의 분산된 데이터를 저장하고 관리하는 데 최적화된 DB이다.\n키-값 저장소, 문서 저장소, 와이드 컬럼 저장소, 그래프 DB 등 다양한 유형이 있으며,\n확장성과 유연한 데이터 모델, 빠른 쓰기/읽기 성능을 특징으로 가진다.\n대표적으로 MongoDB, Cassandra, Redis, Neo4j 등이 있으며, 주로 소셜 네트워크, 실시간 애널리틱스,\n빅데이터 처리 등에 사용된다.\n예를들어 아래와 같은 형태로 데이터가 저장되는 DB로 이해하면 된다.\nex) MongoDB\n# 아래는 MongoDB에서 Json형태로 저장된 데이터형태의 예시이다. { \u0026#34;_id\u0026#34;: \u0026#34;12345\u0026#34;, \u0026#34;name\u0026#34;: \u0026#34;우리흥\u0026#34;, \u0026#34;email\u0026#34;: \u0026#34;sony@naver.com\u0026#34;, \u0026#34;age\u0026#34;: 32, \u0026#34;interests\u0026#34;: [\u0026#34;music\u0026#34;, \u0026#34;football\u0026#34;, \u0026#34;coding\u0026#34;] } ex) Redis\n# 아래는 Redis에서 key:value형태로 저장된 데이터의 예시이다. 다만, 키밸류형태 외에도 str, list, set 등 다양한 형태로 저장될 수 있다. Key: \u0026#34;user:1000\u0026#34; Value: \u0026#34;Sony\u0026#34; ex) Cassandra\n# 아래는 Cassandra와 같은 컬럼 패밀리 데이터베이스에서 사용되는 \u0026#39;열\u0026#39;기반의 구조 저장 방식이다. # 각 컬럼은 컬럼이름, 값, timestamp로 구성된다 { \u0026#34;userId\u0026#34;: \u0026#34;1001\u0026#34;, \u0026#34;columns\u0026#34;: { \u0026#34;name\u0026#34;: \u0026#34;Sony\u0026#34;, \u0026#34;email\u0026#34;: \u0026#34;sony@naver.com\u0026#34;, \u0026#34;age\u0026#34;: 25 } } ex) Neo4j\n# Neo4j와 같은 그래프 데이터베이스는 노드(엔티티), 관계(엔티티간의 연결), 속성(노드와 관계에 대한 정보)으로 데이터를 저장한다. (Node:Player {name: \u0026#34;Sony\u0026#34;, age: 30})-[RELATIONSHIP:FRIEND_OF]-\u0026gt;(Node:Person {name: \u0026#34;Minjae Kim\u0026#34;, age: 25}) 3) 벡터 데이터베이스(VectorDB)\n오늘 알아볼 Vector DB는 AI와 머신러닝 모델에서 생성된 벡터를 효과적으로 관리할 수 있도록 설계된 모델이다.\n높은 차원의 데이터에서도 빠른 검색 속도, 유사 벡터 검색\b 기능을 지원하며,\n대표적으로는 Milvus, Faiss (Facebook AI Similarity Search), Chroma, Qdrant, Pinecone과 같은 vectorDB가 있다.\n주로, 이미지 검색, 추천 시스템, 자연어 처리 등 AI 기반의 애플리케이션에 사용되는 DB이다.\n사실 그냥 정의만 들어서는 Vector DB의 개념과 사용형태에 대해 이해하 잘 되지않을 수 있는데,\n아래 저장되는 형태와 검색과정 예시를 보면 어느정도 감이 잡힐 것이다.\n아래 예시는 가장 대표적인 VectorDB중 하나인 \u0026lsquo;Milvus\u0026rsquo;를 기준으로 데이터를 저장하고 검색하는 과정을 정리하였다.\nfrom milvus import Milvus, DataType # 가장 먼저 Milvus 클라이언트를 초기화한다. # Milvus 서버에 연결 milvus_client = Milvus(host=\u0026#39;localhost\u0026#39;, port=\u0026#39;19530\u0026#39;) ###################################################################### # 뒤에 다루겠지만, 컬렉션이라는 개념에 대해(RDBMS의 테이블과 유사한 개념)정의하고 생성한다. collection_name = \u0026#34;example_collection\u0026#34; fields = [ {\u0026#34;name\u0026#34;: \u0026#34;ID\u0026#34;, \u0026#34;type\u0026#34;: DataType.INT64, \u0026#34;is_primary\u0026#34;: True, \u0026#34;auto_id\u0026#34;: True}, {\u0026#34;name\u0026#34;: \u0026#34;vector\u0026#34;, \u0026#34;type\u0026#34;: DataType.FLOAT_VECTOR, \u0026#34;params\u0026#34;: {\u0026#34;dim\u0026#34;: 128}}, {\u0026#34;name\u0026#34;: \u0026#34;timestamp\u0026#34;, \u0026#34;type\u0026#34;: DataType.INT64}, {\u0026#34;name\u0026#34;: \u0026#34;description\u0026#34;, \u0026#34;type\u0026#34;: DataType.VARCHAR, \u0026#34;params\u0026#34;: {\u0026#34;max_length\u0026#34;: 1024}} ] # 컬렉션 생성 milvus_client.create_collection(collection_name, fields=fields) ###################################################################### # 데이터는 아래와 같이 추가된다 # insert data define vectors = [[0.1, 0.2, ..., 0.128]] # 128차원 벡터 예시 timestamps = [1629264352] descriptions = [\u0026#34;Example vector\u0026#34;] # 데이터 insert insert_data = { \u0026#34;vector\u0026#34;: vectors, \u0026#34;timestamp\u0026#34;: timestamps, \u0026#34;description\u0026#34;: descriptions } milvus_client.insert(collection_name, insert_data) ###################################################################### # 인덱스 생성(인덱스 개념도 뒤에 설명하겠지만, 쿼리에 대한 색인이 가능하게 하는 기준 개념으로 이해하자) index_param = { \u0026#34;index_type\u0026#34;: \u0026#34;IVF_FLAT\u0026#34;, \u0026#34;metric_type\u0026#34;: \u0026#34;L2\u0026#34;, \u0026#34;params\u0026#34;: {\u0026#34;nlist\u0026#34;: 4096} } milvus_client.create_index(collection_name, \u0026#34;vector\u0026#34;, index_param) # 검색 실행 search_param = { \u0026#34;params\u0026#34;: {\u0026#34;nprobe\u0026#34;: 10}, \u0026#34;metric_type\u0026#34;: \u0026#34;L2\u0026#34;, \u0026#34;topk\u0026#34;: 10 } query_vectors = [[0.1, 0.2, ..., 0.128]] results = milvus_client.search(collection_name, \u0026#34;vector\u0026#34;, query_vectors, search_param) Vector DB는 DB에 따라 지원하는 쿼리 검색방법, 특징이 약간씩 다른데, 구체적인 내용은 아래에서 천천히\n확인해보도록 하자.\n자 이제, 위의 각 데이터베이스의 특징을 비교 정리해보면,\n**\u0026lsquo;관계형 데이터베이스\u0026rsquo;**는 정형화된 테이블 구조를 사용하며, 테이블과 테이블간의 관계를 가지며, 높은 무결성을 바탕으로 SQL을 사용하여 데이터를 조회하고 처리할 수 있는 DB를 의미한다.\n**\u0026lsquo;비관계형 데이터베이스\u0026rsquo;**는 관계형 데이터베이스와 달리 테이블간의 관계를 정의하지 않고,\n키-값 저장소, 문서 저장소, 컬럼 패밀리 저장소, 그래프 데이터베이스 등과 같은 다양한 \u0026lsquo;데이터 모델\u0026rsquo;을 사용하여 데이터를 저장하며,\n각 DB고유의 쿼리나 API를 통해 데이터를 핸들링할 수 있는 DB를 의미한다.\n여기서 위의 **\u0026lsquo;다양한 데이터 모델\u0026rsquo;**이라는 개념이 모호한데, 아래 몇가지 예시들을 통해 이해할 수 있을것이다.\n1) MQL\n문서 지향 DB인 MongoDB는 JSON 유사 문서를 사용하며, MQL(MongoDB Query Language)을 사용하여 데이터를\n조회하고 조작한다.\n2) CQL\n또한, 컬럼 패밀리 데이터 모델을 사용하는 Cassandra는 SQL과 유사한 구문을 가진 CQL(Cassandra Query Language)\n을 사용하여 데이터에 접근한다.\n3) Cypher\n그래프 데이터베이스인 Neo4j는 그래프 쿼리 언어인 Cypher를 사용하여 노드, 관계, 패턴을 쿼리할 수 있다.\n4) 간단한 명령어 세트 사용\n키-값 저장소인 Redis는 SQL 혹은 CQL과 같은 전통적인 쿼리 언어 대신, 간단한 명령어 세트를 사용하여 데이터를 조작할 수 있다.\n자, 이제 각 DB가 어떻게 구분되고 사용되어지는지와, VectorDB의 활용성에 대해 대략적으로 이해가 되었을것이다.\n이제부터, 오늘 알아볼 Vector Database에 대해서 구체적으로 살펴보도록 하자.\n2. Vector Database ? # vector db의 가장 큰 특징을 뽑는다면,\n1) 이미지, 오디오, 텍스트 등 다양한 비정형 정보를 고차원의 \u0026lsquo;Vector\u0026rsquo;형태로 저장한다.\n2) ANN(Approximate Nearest Neighbor)과 같은 유사도 기반의 검색방식을 택한다.\n3) 빠른 검색 속도\n정도로 요약할 수 있다.\n\u0026ldquo;이미지, 오디오, 텍스트 등 다양한 비정형 정보를 \u0026lsquo;Vector\u0026rsquo;형태로 저장한다 ?\u0026rdquo; # VectorDatabase는 AI에 적용하기 용이한 특징으로, 최근 주목 받고있는데,\n이는 딥러닝 모델이 생성하는 대량의 고차원 벡터 데이터를 효율적으로 저장하고 검색할 수 있기 때문이다.\n먼저 vectorDB에 데이터를 input하기 위해서는\n이미지, 오디오, 텍스트 등의 데이터는 임베딩이 가능한 딥러닝 모델을 통해 vectorization을 진행할 수 있다.\n모든 딥러닝 모델은 벡터를 통해서 계산되어지고, 특징을 추출할 수 있는데,\nVector DB는 이렇게 생성된 벡터값을 저장, 관리, 사용하는데 강점이 있는 데이터베이스로\n특히, 최근 LLM과 유사도 기반의 검색방식을 통합한 RAG(Retrieval-Augmented Generation)방식이 유행함에 따라,\nvector를 저장하고, 검색할 수 있는 vector DB가 많이 사용되고 있다.\n\u0026ldquo;ANN(Approximate Nearest Neighbor)과 같은 유사도 기반의 검색방식을 택한다\u0026rdquo; # 사실 위 내용에서 ANN 보다도**\u0026lsquo;유사도 기반의 검색방식\u0026rsquo;**을 택한다라는 부분을 기억하자.\nVector DB는 이름그대로 vector를 저장하는 데이터베이스로, 전체 데이터 중 주어진 쿼리와 가장 \u0026lsquo;유사한\u0026rsquo; document를 추출해내는것을\n목적으로하는 데이터베이스이다.\n테이블 구조를 가지고 key를 기반으로 데이터를 얻는 관계형데이터베이스에 반해, Vector DB는 전체 데이터들과 쿼리간의 유사도를 계산하고, 가장 유사도가 높은 document를 추출해야한다는 목적성과 특성때문에\n\u0026lsquo;정확하고 빠른\u0026rsquo; document 추출이 Vector DB 구축에 가장 핵심요소라고 할 수 있다.\n이 때문에, 뒤에 설명할 Vector DB들은 각각 여러가지의 검색 방식을 제공하고 있는데, 대표적인 Vector DB 검색 방식들을 정리해보도록 하자.\n1) 브루트 포스 검색 (Brute-Force Search) == \u0026lsquo;Flat Indexing\u0026rsquo; # 브루트 포스 검색 방식은 모든 데이터포인트를 순회하며, 주어진 쿼리 포인트와의 거리를 일일이 계산한다.\n가장 기본적이고 단순하면서도 확실한 형태의 검색방법인데, 당연히 데이터셋의 크기가 커질수록\n계산량이 기하급수적으로 증가하여 비효율적이다.\n2) Approximate Nearest Neighbor(ANN) # ANN 알고리즘은 가장 널리 사용되는 검색방법으로 \u0026lsquo;완벽한\u0026rsquo; 최근접 이웃을 찾기보다는 \u0026lsquo;근사적으로\u0026rsquo;가장 가까운 이웃을 찾는 방법이다.\n여기서 거리계산의 \u0026lsquo;근사적\u0026rsquo;이라는 표현은 완벽한 정확도를 포기하고, 대신 검색 속도를 크게 향상시키는\n트레이드 오프를 찾는 방식을 의미한다.\n예를들어, 데이터를 미리 클러스터링하여 검색공간을 줄인다거나, 여러 버킷으로 나눈다음, 주어진 쿼리 포인트가 속할 가능성이\n가장 높은 몇 개의 버킷만 검색하는 방식이다.\n대표적으로,\n1) Faiss(Facebook AI Similarity Search)\n2) Annoy(Approximate Nearest Neighbors Oh Yeah)\n와 같은 검색방식들이 이에 해당하는데, 위 검색방식들은 실제로 Vector DB 사용 시, 매우 많이 활용되는 개념으로\n좀 더 자세하게 짚고 넘어가도록 하자.\n1) Faiss(Facebook AI Similarity Search)\nFaiss는 고차원 벡터 공간에서의 유사성 검색과 클러스터링 작업을 위해 설계된 라이브러리로, GPU가속을 지원하며, 양자화(Quantization)와 인덱싱을 통해 메모리 사용량을 최적화를 지원하는 검색 방식이다.\n유사도 검색의 여러방법 중 ANN기법을 사용하는 대표적인 검색시스템이지만, ANN외에도 양자화, 고급 인덱싱기능도 지원한다고\n이해하면 될 것 같다.\n다른 검색방식에 대비된 장점으로는 - 대규모 데이터셋에서 우수한 검색속도 및 정확도\n- GPU를 통한 검색 가속 지원\n- 다양한 인덱싱 방법을 제공\n이 있으며, 단점으로는 오히려 다양한 인덱싱 옵션과 파라미터 설정이 다소 복잡하게 느껴질 수 있고, 일부 인덱싱방법은 메모리 사용량이 많을 수 있다는 점이 꼽힌다.\n아래에 Faiss 라이브러리를 통해 유사 벡터를 검색하는 과정을 살펴보자.\nimport numpy as np import faiss dimension = 64 # 벡터 차원 n = 10000 # 데이터베이스 크기 np.random.seed(123) db_vectors = np.random.random((n, dimension)).astype(\u0026#39;float32\u0026#39;) query_vectors = np.random.random((1, dimension)).astype(\u0026#39;float32\u0026#39;) # 인덱스 생성 및 벡터 추가 index = faiss.IndexFlatL2(dimension) # L2 거리를 사용하는 flat 인덱스 index.add(db_vectors) # 쿼리에 대한 가장 가까운 이웃 검색 k = 4 # 찾고자 하는 이웃의 수 D, I = index.search(query_vectors, k) # D: 거리, I: 인덱스 print(\u0026#34;Nearest neighbors: \u0026#34;, I) print(\u0026#34;Distances: \u0026#34;, D) **2) Annoy(Approximate Nearest Neighbors Oh Yeah)**Annoy는 메모리 사용량을 최적화하면서 대용량 데이터셋에서도 빠르게 검색할 수 있는 방법으로, 트리를 기반으로 한 인덱스 구조를 사용하여, 검색 시간과 메모리 사용량 사이의 균형을 맞춘다. 장점으로는\n- 디스크에 인덱스를 저장하고, 메모리 매핑 방식을 사용하여, 메모리 사용량을 최적화할 수 있다는 점\n- 설정 방식이 간단하다는 점\n- 데이터를 실시간으로 추가하고, 인덱스를 업데이트할 수 있다는 점\n정도가 꼽힌다.\n다만, 단점으로\n- GPU가속을 지원하지 않아, Faiss에 비해 대규모 데이터셋 처리 시, 검색속도가 느릴 수 있다는 점\n- Annoy는 근사 검색 방식으로 동작하기때문에, Faiss에 비해 다소 정확도가 낮을 수 있다는 점이 꼽힌다.\n아래는 Annoy 라이브러리를 통해서 유사 데이터 포인트를 추출하는 예시코드이다.\nfrom annoy import AnnoyIndex import random dimension = 40 n_trees = 10 index = AnnoyIndex(dimension, \u0026#39;euclidean\u0026#39;) # 데이터 추가 for i in range(1000): vector = [random.gauss(0, 1) for z in range(dimension)] index.add_item(i, vector) index.build(n_trees) # 쿼리 벡터 query_vector = [random.gauss(0, 1) for z in range(dimension)] # 가장 가까운 이웃 검색 nearest_neighbors = index.get_nns_by_vector(query_vector, 5, include_distances=True) print(\u0026#34;Nearest neighbors: \u0026#34;, nearest_neighbors)​ 3) 고급 인덱싱 기반 검색 # 데이터를 구조화하여 검색속도를 향상시키는 방법이다. 즉, 데이터를 미리 인덱싱하여 검색 시간을 단축시키는 방법인데,\n예를들어 위의 Faiss의 경우, 데이터를 군집화하여 검색범위를 좁히는 방식인 IVF(Inverted File Index)를 통해 검색범위를 좁혀 ANN(최근접 이웃 검색)을 구현하거나, 벡터를 양자화해서 메모리사용을 줄이는 PQ(Product Quantization)방식을 통해 ANN을 구현할 수 있는 것이다.\n아래는 고급 인덱싱 기반 검색 방법들에 대한 설명이다.\n1) IVF(Inverted File Index)\nIVF방식은 데이터셋을 미리 정의된 수의 군집(centroid)으로 나누고, 각 벡터를 가장 가까운 centroid에 할당함으로써 검색범위를 제한하는 방식으로 ANN에 공헌한다. 검색 시, 쿼리벡터가 속하는 군집을 먼저 찾고, 해당 군집내에서 가장 유사한 벡터를 찾는 방식이라고 이해하면 된다.\n빠른 검색속도를 구현했지만, 매 검색 시 적절한 군집수를 선택해야하고, 군집화에 시간이 소요될 수 있다는 점이 단점이다.\n2) PQ(양자화 기반 검색, Product Quantization)\nPQ방식은 고차원 벡터를 여러개의 하위 벡터로 분할하고, 각 하위 벡터를 양자화하여 저장공간을 줄이는 방식이다. 조금 더 구체적으로 설명하면, 예를들어 2차원 벡터공간에서 4개의 코드워드로 구성된 코드북을 생성했다고 가정하자.\n이때, 코드워드는 벡터공간내의 대표 포인트를 의미하는데, centroid와 비슷한 개념으로 이해했다.\n그리고 코드북은 이러한 코드워드들의 집합이다.\n결국 모든 포인트는 가장 가까운 코드워드로 대체되며 전체 데이터포인트의 크기는 줄어들게 된다.\n이를 양자화로 표현하는것이고, 이로 인해 줄어든 공간에서의 검색속도 또한 범위가 줄어들기 때문에 빨라질 수 밖에 없\b다는\n원리이다.\n3) HNSW(Hierarchical Navigable Small World)\nHNSW방식은 다계층 그래프 구조를 사용하여, 고차원 데이터에 대한 효윬적인 최근접 이웃 검색을 수행하는 방법이다.\nHNSW방식은 속도도 빠르고, 다른 인덱싱 기법에 비해 정확하다는 강점을 가진다.\n4) LSH(Locality-Sensitive Hashing)\n데이터 포인트를 해시 테이블에 매핑하여, 쿼리 벡터와 유사한 데이터 포인트들을 빠르게 검색하는 방법이다. LSH는 유사한 객체가\n\u0026lsquo;같은 버킷\u0026rsquo;에 할당될 확률을 높이는 해시 함수를 사용한다.\n지금까지, Vector DB의 특성과 Vector DB를 검색하는 여러 방법론들에 대해서 알아보았다.\n분량이 많아, 각 Vector DB들의 비교정보는 다음에 포스팅 하도록 하겠다.\nVector DB 별 비교 정리 바로가기\u0026gt; https://familia-89.tistory.com/90\nReference # https://www.elastic.co/kr/what-is/vector-database\nhttps://hotorch.tistory.com/407\nhttps://hotorch.tistory.com/406\n","date":"2024.02.27","externalUrl":null,"permalink":"/posts/89/","section":"글","summary":"","title":"Vector DB란 무엇일까? 그리고 어떤 Vector DB를 사용해야할까? (1)","type":"posts"},{"content":"","date":"2024.02.23","externalUrl":null,"permalink":"/tags/feature-tokenizer/","section":"Tags","summary":"","title":"Feature Tokenizer","type":"tags"},{"content":"","date":"2024.02.23","externalUrl":null,"permalink":"/tags/ft-transformer/","section":"Tags","summary":"","title":"Ft-Transformer","type":"tags"},{"content":"","date":"2024.02.23","externalUrl":null,"permalink":"/tags/tabular/","section":"Tags","summary":"","title":"Tabular","type":"tags"},{"content":"","date":"2024.02.23","externalUrl":null,"permalink":"/tags/transformer/","section":"Tags","summary":"","title":"Transformer","type":"tags"},{"content":" Paper : https://arxiv.org/abs/2106.11959v5\n최근 vision과 NLP등 ML Task에서는 거의 모든 task에서 Deep learning Architecture가 활용되며\n다양한 아이디어의 모델들이 제안되고, 연일 해당 Task의 SOTA를 갱신하고 있다.\nDL architecture의 경우, loss를 통한 weight 와 bias의 미분 값을 계산하고 업데이트하는 방식을 자동으로 구현한, AutoGrad를 통해 End-to-End방식의 다양한 구조의 모델을 유연하게 결합하며 다양한 Task에서 좋은 성능을 내고있다.\n하지만 Tabular Data를 다루는 분야에서는 여전히 딥러닝 보다는 GBDT계열의 모델들이 여전히 강세를 보이는데, 이따금 GBDT계열의 모델보다 좋은 성능을 보이는 딥러닝 모델이 있다 하더라도, 그 차이가 미미하거나, 다양한 Task에서 안정적으로 성능을 보이지 못하는 경우가 많아, 더더욱 GBDT계열의 모델이 많이 쓰이고 있다. (속도도 GBDT가 더 빠르다)\n다만, LightGBM, XGBoost와 같은 GBDT계열 모델들은 미분계산을 통한 가중치 업데이트가\n불가하기때문에, End-To-End 방식의 복수 모델들의 결합 방식의 Architecture 구현이 불가능하다.\n이로 인해, 텍스트 데이터를 함께 학습하거나, 다른 모듈(모델)과 결합이 필요한 작업에서는\n제한적으로 사용하거나, 다른 딥러닝 계열의 모델로 대체를 고려할 수 밖에 없다.\n나 또한, 현재 회사가 적재한 데이터의 형태가 대부분 Tabular dataset이라,\n이런 부분에 항상 아쉬움을 느끼는데, 다른 모델과의 End-To-End 방식의 결합 학습을 위해\n보통 MLP를 많이 사용하지만, 개별 모델의 성능을 봤을때, 대부분 케이스에서\n부스팅 계열의 모델보다 속도와 성능 모두 떨어진다.\n언뜻 생각하기엔 여러층의 layer를 쌓아올린 MLP 구조와 activation function를 사용한 MLP 모델이,\n깊은 레이어에 대해 복잡한 가중치 미분 계산을 통한 업데이트 방식이 가능하고, 비선형적 데이터도 학습이 가능한점으로 인해,\n성능이 더 좋을것 같다고 생각할수도 있다. 그럼에도 GBDT계열의 모델에 비해, 기대만큼 성능이 좋지않은 이유는,\n구조적으로 \u0026lsquo;특성간의 복잡한 곱셈적 상호작용\u0026rsquo;을 직접적으로 모델링하는 것이 힘들기 때문이다.\n무슨말인지\u0026hellip; 아래\b 예시를 살펴보자.\n다음과 같이 두가지 feature 값 X_1, X_2가 있다고 가정해보자.\n만약 이 두 특성이 각각 판매물건개수와 물건단가라면,두 특성의 곱셈을 통해 \u0026lsquo;총 상품매출액\u0026rsquo;이라는 정보를 얻을 수 있고,\n이 정보는 모델이 해결하고자하는 목표에 유의미한 정보가 될 수 있다.\nX_1 x X_2 = \u0026lsquo;총상품매출액\u0026rsquo;\n각 특성정보간의 곱셈을 통해서 관계성에 대한 정보를 추가로 학습할 수 있게 되는데,\n가장 대표적으로 Transformer의 scaled-dot product attention 계산과정이 이러한 특성간 곱셈적 상호작용(행렬곱)을\n학습할 수 있게끔 한다.\n반대로, MLP의 각 layer에서 위와 같은 두 특성을 학습할때는 아래 수식과 같은 \u0026lsquo;가중합\u0026rsquo; 과정을 통해 학습을 진행하게 된다.\ny = w_1 * X_1 + w_2 * X_2 + b\n위 수식과 같이, w_1과 w_2는 각 특성에 대한 weight이고, b는 bias를 의미할때, 각 입력 특성에 weight를 곱한 값들을\n모두 더한 다음, bias를 더해 출력 y를 계산하게 된다.\n그리고, 출력된 y는 비선형 activatioin function을 통과하게 된다.\n자. 이와 같이, 계산과정에서 보듯, MLP의 layer단에서는 가중합(선형조합)과 비선형 activation function을 적용할 뿐,\n곱셈적 상호작용에 대한 연산과정이 없는것을 볼 수 있다.\n따라서, MLP architecture에서는 각 feature간의 관계성에 대한 학습이 어려울 수밖에 없는것이다.\n자, MLP의 문제점을 설명하고, 결국, 미분계산이 가능한 딥러닝 모델이 필요한 이유를 정리하다보니,,, 서론이 길어졌다.\n이제 위 문제들을 해결할 DL Architecture를 알아보도록 하자.\n오늘은 Tabular dataset을 학습할 수 있는 DL Architecture중 Feature Tokenizer개념과 Transformer Architecture를 적용하여\nSOTA 수준의 성능을 보이는 **\u0026lsquo;FT-Transformer\u0026rsquo;**에 대해 알아보려한다.\nFT-Transformer란? # FT-Transformer는 \u0026lsquo;Feature Tokenizer\u0026rsquo;와 Transformer 두 부분으로 이루어진 아키텍처로,\n정형 데이터를 위한 transformer 아키텍처의 간단한 변형 버전이다.\nFT-Transformer Architecture\n위 그림을 보면 input되는 각 특성에 대해서 \u0026lsquo;Feature Tokenizer\u0026rsquo;라는 과정을 거친 출력값을\nTransformer에 input하여 관계성을 학습하게 된다.\n먼저 Feature Tokenizer 개념에 대해 알아보자.\n(1) Feature Tokenizer # Feature Tokenizer라는 개념은 수치형(numerical) feature와 범주형(categorical)feature를 다루기 위해\n설계된 개념이다. 수치형과 범주형같은 다양한 유형의 데이터를 Transformer에서 효율적으로 학습할 수 있도록\n인코딩(임베딩) 하는 역할을 수행한다. 결국 Feature Tokenizer를 통해 각 특성은 각각 고유한 방식으로 \u0026lsquo;토큰화\u0026rsquo;되어\nTransformer의 입력으로 사용할 수 있는 형태로 변환된다.\nFeature Tokenizer\n위 그림은 Feature Tokenizer 프로세스의 모식도이다.\n먼저 왼쪽의 붉은 블럭들은 numerica feature의 value를 의미하고, 초록색 블럭은\ncategorical feature의 value를 의미한다.\n위 Feature Tokenizer의 진행순서대로 따라가보자.\n[진행순서]\n1) Numerical Feature\nnumerical feature의 경우 일반적으로 먼저, 정규화나 표준화 과정을 거친 후에 들어오게된다.\n즉, FT-Transformer에 input되기전에 scailing은 선행되어야한다는 뜻이다.\n이렇게 정규화가 완료된 numerical feature value에 \u0026lsquo;*임베딩 행렬\u0026lsquo;을 곱해서\n해당 numerical feature에 대응되는(임베딩한) \u0026lsquo;d차원의\u0026rsquo; dence vector를 생성하는 과정을 진행하게 된다.\n* 여기서 \u0026lsquo;임베딩 행렬\u0026rsquo;은 지난번에 포스팅 했던 Weight initialization으로 생성된 기초 매트릭스를 의미한다. 잊지말자!\n이 과정이 위 그림에서 붉은색 블럭들의 계산과정에 해당한다.\n2) \bCategorical Feature\nCategorical Feature는 조금 다른 방식으로 임베딩을 진행하게 된다.\n먼저, 마찬가지로 Feature Tokenizer에 들어오기 전에 Categorical Feature의 value또한\nlabel encoding또는 onehot-encoding을 먼저 선행해야 한다.\n그리고 Feature Tokenizer에서 각 범주형 feature의 unique값들에 대해 index를 부여한다.\n예를들어, \u0026lsquo;병명\u0026rsquo;이라는 범주형 변수에서 unique value는 \u0026lsquo;당뇨병\u0026rsquo;과 \u0026lsquo;심근경색\u0026rsquo;, \u0026lsquo;고혈압\u0026rsquo; 이라고 가정하자.\n이 당뇨병과 심근경색, 고혈압이라는 Value에 0(당뇨병), 1(\b심근경색), 2(고혈압)과 같은 index를 부여한다.\n그리고 마찬가지로 임베딩 행렬을 통해 범주형 변수를 임베딩하게 되는데, numerical feature와는 조금 방식이 다르다.\n위에서 각 unique value에 지정했던 index를 \u0026lsquo;행\u0026rsquo;으로 간주한다.\n그리고, 임베딩 행렬의 index에 해당하는 \u0026lsquo;행\u0026rsquo;의 vector가 바로 그 unique value의 \u0026lsquo;임베딩 벡터\u0026rsquo;가 된다.\n예를들어 위 0(당뇨병), 1(\b심근경색), 2(고혈압)으로 부여된 index에 아래 임베딩 행렬을 적용한다고 한다면,\n\b심근경색(index : 1)의 임베딩 벡터는 행 \u0026lsquo;1\u0026rsquo;에 해당하는 [0.4, 0.5, 0.6]이 된다.\n이 과정이 바로 위 그림의 초록색 블럭들의 계산과정에 해당한다.\n자, 이제 Transformer에 input하기 위한 Feature Tokenizer 과정이 모두 끝났다.\n마지막으로 Feature Tokenizer의 효과에 대해서 간략하게 정리하고 넘어가도록 하자.\n\u0026lt;Feature Tokenizer의 효과\u0026gt;\nFeature Tokenizer를 사용함으로써, 모델은 다양한 유형의 feature를 효율적으로 처리할 수 있다. Transformer가 처리할 수 있는 통합된 input 데이터 형태로 변환할 수 있게 된다. (핵심) 특히, 범주형 데이터의 풍부한 의미적 표현을 학습할 수 있게 된다. (2) Transformer # \u0026ldquo;FT-Transformer내부의 Transformer는 전통적인 Transformer와는 조금 다르게 구성된다\u0026rdquo;\n-\u0026gt; Position Embedding 이 없다!\n일반적인 Transformer 모델에서는 각 token들에 대해 Position Embedding을 적용하여,\n입력된 시퀀스의 순서정보를 모델에 투입한다.\n하지만, 이미 정형데이터로 구조화된 Tabular데이터에는 입력 특성간의 순서 정보는 사실 별로 중요하지않거나,\n고정되어있고, 의미도 가지지 않는 경우가 많다.\n따라서 FT-Transformer에는 Position Embedding 과정은 생략된다.\n-\u0026gt; Feature-wise Self-Attention을 통해 Feature간의 관계성을 학습한다.\n이 부분은, 일반적인 Transformer의 scaled-dot self-attention과 동일하다.\ntoken이 feature의 value로 대체된다는 점만 바뀐다.\n예를들어, 특성벡터가 [\u0026lsquo;나이\u0026rsquo;, \u0026lsquo;성별\u0026rsquo;, \u0026lsquo;BMI\u0026rsquo;]이고 각 특성을 스칼라 값으로 표현한다고 가정하자.\n[10, 20, 30]으로 표현하자.\nFeature-wise Attention Score를 계산하기 위해 \u0026lsquo;나이\u0026rsquo;가 \u0026lsquo;성별\u0026rsquo;, \u0026lsquo;BMI\u0026rsquo;에 미치는 영향을 계산하기 위해,\n나이와 성별, 나이와 BMI간의 \u0026lsquo;내적\u0026rsquo;을 계산한다.\nex)\n나이 x 성별 = 10 x 20 = 200 나이 x BMI = 10 x 30 = 300 이후, Softmax를 적용(계산 생략하고)하면,\n\u0026lsquo;성별\u0026rsquo;에 대한 attention score는 0.4\n\u0026lsquo;BMI\u0026rsquo;에 대한 attention score 는 0.6 으로 구하는 가정을 세워볼 수 있다.\n결국, 일반적인 Self-Attention은 시퀀스내의 위치에 기반한 관계(예:문장 내 단어 순서)를 모델링한다면,\nFeature-wise Self-Attention은 각 feature가 다른 feature에 미치는 영향을 직접 계산함을 통해서,\n각 feature간의 상호작용을 학습할 수 있게 되는것이다.\n마치며 # 오늘은 FT-Transformer에 대해 알아보는 시간을 가졌다.\nnumerical feature와 categorical feature가 혼용되어 있는 Tabular Dataset을 학습할때 매우 유용한 모델이라고 생각되고,\n실제로 테스트해본 결과, GBDT계열의 모델모다 거의 대부분의 케이스에서 좀 더 좋은 성능을 보이는것을 확인할 수 있었다.\nMLP를 확실히 대체할만한 DL architecture인것 같아, 앞으로 많이 사용하게 될 것 같다.\nReference # Paper : https://arxiv.org/abs/2106.11959v5\nhttps://velog.io/@kyyle/%EC%BD%94%EB%93%9C-%EA%B5%AC%ED%98%84-FT-Transformer\n","date":"2024.02.23","externalUrl":null,"permalink":"/posts/87/","section":"글","summary":"","title":"이제 Tabular Data는 MLP 대신 FT-Transformer를 써야하는 이유!","type":"posts"},{"content":"","date":"2024.02.22","externalUrl":null,"permalink":"/tags/garbage-collection/","section":"Tags","summary":"","title":"Garbage Collection","type":"tags"},{"content":" https://velog.io/@gayeong39/Garbage-Collection 참조\n개발업무를 진행하다보면, 불필요한 작업 또는 데이터로 인해 메모리 리소스가 부족할때가 종종 있다.\n이때문에 개발자는 항상 할당중인 메모리를 지속적으로 모니터링하고 관리해야 한다.\n오늘 알아볼 Garbage Collection, 이하 GC라는 개념은 프로그래밍에서 자동으로 메모리 관리를 수행하는\n프로세스를 의미한다. 프로그램이 실행되는 동안 동적으로 할당된 메모리(힙 메모리)영역에 객체들이 생성되는데,\n이 중 *더 이상 필요하지않게 된 메모리(아래에서 구체적으로 설명하겠다)와 같은 garbage, 즉, 쓰레기가 된 메모리를\n자동으로 식별하고 회수하는 역할을 한다.\n1. 그런데, **\u0026lsquo;어떤 객체\u0026rsquo;**를 **\u0026lsquo;어떤 근거\u0026rsquo;**로 garbage로 판단하게 되는걸까? # 먼저, GC의 기본 원리는 **\u0026lsquo;도달 가능성(reachability)\u0026rsquo;**을 근거로 가비지 메모리의 여부를 판단하게 된다.\n여기서 도달가능성이란 특정 메모리 객체가 프로그램의 루트집합(root set)으로부터 직접적 또는\n간접적으로 접근할 수 있는 경로가 하나라도 있다면 이 메모리는 **\u0026lsquo;도달가능성\u0026rsquo;**이 있는 객체로 간주한다.\n이 루트 집합에는 **\u0026lsquo;글로벌 변수\u0026rsquo;, \u0026lsquo;지역변수\u0026rsquo;, \u0026lsquo;활성메소드의 매개변수\u0026rsquo;**가 포함되는데, 아래 예시\b들에 해당하는 요소들을 말한다.\n# global variable 예시 global_list = [1, 2, 3] # 프로그램 전체에서 접근 가능한 리스트 def use_global(): print(global_list) use_global() # [output] # [1, 2, 3] # local variable 예시 def local_example(): local_var = \u0026#34;Hello\u0026#34; # 지역 변수 print(local_var) local_example() # [output] # Hello # 활성 메소드의 매개변수(매서드내 parameter) def param_example(param): print(param) param_example(\u0026#34;This is a parameter\u0026#34;) 즉, 위와 같은 루트 집합(root set)의 요소들로 부터 직접적이거나 간접적으로 접근할 수 있는 경로가 있을때 이 객체를\n우리는 흔히 **\u0026lsquo;참조\u0026rsquo;**하고 있는 객체라고 표현한다.\n참조 중인 객체는 **\u0026lsquo;사용중인 객체(메모리)\u0026rsquo;**라는 의미로 볼 수 있고, 해당 메모리는 GC에서 제거할 대상이 아닌것이다.\n그리고, 이 반대에 해당하는, 루트 집합의 요소들중 **\u0026lsquo;참조 되고있지 않은\u0026rsquo;, \u0026lsquo;접근할 수 있는 경로가 없는\u0026rsquo;**객체의 경우\nGC에서 삭제 대상이 되는 객체로 분류할 수 있는 것이다.\n그럼, 간단한 예시를 통해 살펴보자.\n1) 초기상태 # 루트집합 -\u0026gt; A -\u0026gt; B -\u0026gt; C 예를들어, 객체 A, B, C가 있고, A는 B를 참조하고, B는 C를 참조하고 있다고 가정하자.\n그리고, 루트집합에는 객체 A만 포함되어 있다.\n2) 변경발생(Garbage 발생) # 루트집합 -\u0026gt; A B -\u0026gt; C 그리고, 작업 진행도중 객체 A가 B를 더이상 참조하지 않게 된다.\n이제 루트 집합에서 B나 C로 갈 수 있는 경로가 사라졌다.\n위 케이스에서 Garbage Collection은 어떻게 동작하게 될까?\nGC가 참조되지 않는 객체를 추적하는 과정은 크게 \u0026lsquo;마킹(Marking)\u0026lsquo;과 \u0026lsquo;스위핑(Sweeping)\u0026lsquo;단계로 구분할 수 있다.\n먼저 \u0026lsquo;마킹\u0026rsquo;단계에서는,\n루트집합에서 시작하여 접근할 수 있는 모든 객체에 대해 \u0026lsquo;탐색\u0026rsquo;을 수행한다.\n가비지 컬렉터는 이 루트 집합으로 부터 출발하여 객체들을 따라가며(reachable objects), 각 객체에\n\u0026lsquo;마킹\u0026rsquo;을 한다. 이렇게 해서 도달할 수 잇는 모든 객체가 마킹되며, 도달할 수 없는 객체들은 마킹되지않은\n상태로 남게된다.\n그 다음, \u0026lsquo;스위핑\u0026rsquo;단계가 진행된다.\n스위핑 단계에서는 힙 메모리 전체를 스캔하며, 마킹되지 않은 객체들을 찾아 메모리를 회수한다.\n이 과정에서 실제로 해당 객체가 사용하고 있던 메모리가 해제되며, 다른 목적으로 재사용될 수 있게 된다.\n위 예시에서 마킹과 스위핑이 진행되는 과정을 적용해보면,\n1) 가비지 컬렉터가 작동을 시작하면, 루트집합에 있는 A부터 탐색을 진행한다.\nA는 도달가능한 객체이므로 마킹된다.\nB와 C는 루트집합에서 도달할 수 없으므로 마킹되지않고,\n2) 스위핑 단계에서 마킹되지 않은 B와 C객체의 해당 메모리는 회수되게 된다.\n2. Python에서는 gc 모듈을 통해 garbage collection을 적용할수 있다. # 파이썬의 \u0026lsquo;gc\u0026rsquo; 모듈을 사용하면 가비지 컬렉터에 대한 제어와 가비지 컬렉션 프로세스에 대한 정보를\n얻을 수 있다. 이를 통해 특정 객체에 대한 참조 체인을 확인하거나, 어떤 객체들이 현재 도달 가능한 상태인지를 파악할 수 있다.\n1) 객체 참조 체인 확인하기 # import gc class MyClass: pass # 객체 생성 obj = MyClass() ref = obj # obj에 대한 추가 참조 생성 # 가비지 컬렉션 활성화 및 객체 추적 gc.collect() gc.set_debug(gc.DEBUG_LEAK) # obj에 대한 참조자 조회 referrers = gc.get_referrers(obj) print(f\u0026#34;obj의 참조자: {referrers}\u0026#34;) # obj가 참조하는 객체 조회 referents = gc.get_referents(obj) print(f\u0026#34;obj가 참조하는 객체: {referents}\u0026#34;) 위 gc 모듈을 사용한 코드를 통해 MyClass클래스의 변수인 obj가 참조중인 객체들의 목록과 obj를 참조하고있는 객체들의 목록을 차례로\n출력해볼 수 있다.\ngc.collect() 마지막으로 gc.collect()라는 클래스를 호출하게 되면, 위에서 설명한 garbage collection 과정을 수행하여,\n참조되지않은 객체, 비 참조 메모리를 모두 회수할 수 있게 된다.\nReference # https://medium.com/dmsfordsm/garbage-collection-in-python-777916fd3189\nhttps://www.winterjung.dev/python-gc/\nhttps://velog.io/@gayeong39/Garbage-Collection%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89%EC%85%98\n","date":"2024.02.22","externalUrl":null,"permalink":"/posts/86/","section":"글","summary":"","title":"Garbage Collection(가비지 컬렉션, GC)은 어떻게 동작할까?","type":"posts"},{"content":"","date":"2024.02.22","externalUrl":null,"permalink":"/tags/garbage-collector/","section":"Tags","summary":"","title":"Garbage Collector","type":"tags"},{"content":"","date":"2024.02.22","externalUrl":null,"permalink":"/tags/gc/","section":"Tags","summary":"","title":"Gc","type":"tags"},{"content":"","date":"2024.02.22","externalUrl":null,"permalink":"/tags/%EA%B0%80%EB%B9%84%EC%A7%80%EC%BB%AC%EB%A0%89%EC%85%98/","section":"Tags","summary":"","title":"가비지컬렉션","type":"tags"},{"content":"","date":"2024.02.22","externalUrl":null,"permalink":"/tags/%EA%B0%80%EB%B9%84%EC%A7%80%EC%BB%AC%EB%A0%89%ED%84%B0/","section":"Tags","summary":"","title":"가비지컬렉터","type":"tags"},{"content":"","date":"2024.02.17","externalUrl":null,"permalink":"/tags/he/","section":"Tags","summary":"","title":"He","type":"tags"},{"content":"","date":"2024.02.17","externalUrl":null,"permalink":"/tags/lecun/","section":"Tags","summary":"","title":"Lecun","type":"tags"},{"content":"","date":"2024.02.17","externalUrl":null,"permalink":"/tags/relu/","section":"Tags","summary":"","title":"Relu","type":"tags"},{"content":"","date":"2024.02.17","externalUrl":null,"permalink":"/tags/sigmoid/","section":"Tags","summary":"","title":"Sigmoid","type":"tags"},{"content":"","date":"2024.02.17","externalUrl":null,"permalink":"/tags/tanh/","section":"Tags","summary":"","title":"Tanh","type":"tags"},{"content":"","date":"2024.02.17","externalUrl":null,"permalink":"/tags/weight-initialization/","section":"Tags","summary":"","title":"Weight Initialization","type":"tags"},{"content":"최근 딥러닝 개발을 진행할때 사용하는 현대의 딥러닝 라이브러리에서는 대부분 가중치 초기화 과정을 자동으로 제공하도록\n라이브러리 자체에 기능을 내장하고 있다.\n대표적으로 Pytorch에서 제공하는 linear layer(nn.Linear)는 내부에 가중치 초기화 기능을 내장하고 있다.\n(물론, torch.nn.init 모듈 내에서 default 가중치 초기화 방식을 변경할 수 있다)\n그래서, 딥러닝 개발과정에서 가중치 초기화 과정에 대해 전혀 관여하지 않고 모델을 개발하는 경우가 많다.\n다만, 그렇다고 가중치 초기화 작업이 중요하지 않다는 것은 아니다.\n딥러닝 라이브러리의 버전이나 종류에 따라서 가중치 초기화는 각각 다른 방법을 채택하고 있을 수 있고,\n데이터의 특성 또는 과제의 특성에 따라 이에 적합한 가중치 초기화 방법을 직접 구축하고 테스트하는것은\n모델, 데이터, 과제에 따라 매우 중요하게 고려되어야할 요소일 수 있다.\n오늘은 **\u0026lsquo;가중치 초기화(Weight initialization)\u0026rsquo;**의 개념과 필요성에 대해서 살펴보도록 하자.\n가중치 초기화는 말 그대로, 딥러닝 모델을 학습하기 전 초기 weight값을 특정 값으로 초기화하는 방법을 의미한다.\n적합한 가중치 초기화 방법의 선택 여부에 따라서, 모델의 최종 학습 성능이 달라질 수 있고, 학습 과정에서 gradient vanishing, exploding과 같은 문제로 인해 local minimum에 빠지는 문제도 예방할 수 있게된다.\n아래 표는 가중치초기화 여부 또는 방식에 따라 모델의 최종 성능이 얼마나 달라질 수 있는지를 잘 보여준다.\nweight initialization에 따른 모델 성능비교 (출처 : https://lv99.tistory.com/23)\n\b먼저, 가중치 초기화의 안 좋은 예시를 몇가지를 살펴보자.\n# weight 초기값을 모두 0으로 설정한 경우 # 만약 가중치를 0으로 초기화한 후 학습을 진행한다면, 모든 뉴런들이 같은 값을 가지게 되고, Back propagation 진행 시, 각 뉴런이 loss에 미친 영향도는 같으므로, 모두 동일한 값으로 가중치가 update된다.\n이렇게 모든 레이어가 동일한 값으로 update된다는 것은 레이어를 여러개의 층으로 나눈 의미를 퇴색되게끔한다.\n즉, 모든 노드들이 같은 값을 가지게되어 다양한 의미의 학습이 되지않고, 거기에 값이 모두 0이기때문에 연산이 무효화 되어 학습이 진행되지않는 것이다.\n# weight 초기값을 너무 크게 or 작게 설정한 경우 # 예를들어 초기화 값이 너무 크게 설정된 경우, gradient가 기하급수적으로 커지며, weight도 비정상적으로 커지면서 학습 양상이 발산하게 된다. 반대로 초기값을 너무 작게 설정한 경우에도 역전파과정에서 입력층으로 연산이 진행될 수록 gradient가 작아지면서 weight가 제대로 업데이트되지 않게 된다.\n따라서, 역전파과정에서 다양한 의미를 학습하여, 복수의 depth로 이루어진 layer를 통해 효과적으로 가중치를 오래 update할 수 있도록\n최초 가중치 설정이 매우 중요한것이다.\n그렇다면, 이제 대표적으로 활용되는 가중치 초기화 방법에는 어떤 것들이 있는지 한번 살펴보도록 하자!\n1) LeCun # LeCun 공식\n98년도에 Yann LeCun이 제기한 방법으로 최근에는 Xavier나 He 방식에 비해 덜 사용된다고 한다.\nLeCun은 주로 sigmoid activate function과 함께 사용될 때, 신경망이 효율적으로 학습할 수 있도록 설계되어 있다.\n이 방식은 입력 차원의 수(n_in)를 기반으로 가중치의 분산을 조정해서, 네트워크의 각 레이어를 통과할때,\n입력 신호의 분산이 유지되도록 하는 방법이다.\n다만, 시그모이드나 하이퍼볼릭 탄젠트와 같은 S자형 활성함수와 사용하게 될 경우 gradient vanishing문제를 완화하는데 도움을 주지만,\nReLU와 같은 비선형 활성함수와 함께 사용할 경우 gradient vanishing, exploding 문제를 해결하기에 충분하지 않을 수 있다.\n입력 차원이 10인 레이어의 경우 가중치를 초기화할 표준편차를 구하는 방법은, 아래와 같이 계산된다.\n2) Xavier (Glorot) # 이 방법은 입력 노드와 출력 노드에 따라 초기 가중치의 스케일을 조정하는 방법이다.\n위 공식에서 n-in은 입력 차원의 수, n-out은 출력차원의 수를 의미한다.\n이는 feedforward방향과 backpropagation 방향에서 정보(신호)가 적절히 전파될 수 있도록 입력차원과 출력차원의 고려하여\n가중치가 분산된 값을 가질 수 있도록 고안된 방법이다.\n해당 방식은 하이퍼볼릭 탄젠트와 같이 중심값이 0인 활성함수와 함께 활용하기 적합한 초기화 방식이다.\n다만, ReLU와 같은 비선형 활성함수와 함께 활용될 경우 gradient vanishing 문제를 해결하기 어렵다는 단점을 가지고 있다.\n이는 Xavier의 초기화가 가중치의 분산을 he초기화에 비해 작게 가져가게 되는데, ReLU를 사용할때는 초기 가중치가\n너무 작으면, 네트워크의 초기 단계에서 많은 뉴런이 0을 출력하게 되어 네트워크 학습이 제대로 이루어지지 않을 수 있기때문이다.\n이는 결국 깊은 네트워크에서 정보의 학습이 제대로 진행되지않는 문제로 이어지게 된다.\n3) He (Kaiming) # ReLU 활성화 함수와 그를 변형한 활성함수를 사용하는 네트워크를 위해 개발된 초기화 방식이다.\nReLU는 음수 입력에 대해 0을 출력하기 때문에, Xavier 초기화 방식이 제공하는 분산 조정은 ReLU를 사용하는 데 적합하지 않다.\nHe 초기화는 이를 고려하여 입력 유닛의 수를 기반으로 가중치의 분산을 결정하며, 아래의 *공식을 활용해서 ReLU의 음수 입력시,\n0을 출력하는 문제로 발생하는 neuron dying문제를 완화하기 위해 더 큰 분산을 사용하여 가중치를 초기화 하는 방식을 선택한다.\n계산 방식은 아래와 같다.\n예를들어, 입력차원이 10인 경우 계산된 가중치의 표준편차는 아래와 같이 계산된다.\nHe 초기화 방식은 위 설명과 같이 ReLU 활성함수와는 궁합이 잘 맞지만, 반대로 Sigmoid나, tanh과 같은 S형 활성함수를\n사용하는 네트워크에는 적합하지 않은데, He초기화 방식이 Xavier과 같은 초기화 방식에 비해 큰 분산을 사용하여 가중치를\n초기화하기때문에, 네트워크 초기에 너무 큰 가중치 값으로, tanh나 sigmoid함수가 빠르게 포화 상태에 도달하게 되어\ngradient vanishing문제를 악화시킬 수 있기 때문이다.\n# 모델을 학습할때, 가중치 초기화 방식에 문제가 있다는 것은 어떻게 탐지할 수 있을까? # 가중치 초기화가 매우 중요하다는 것은 충분히 이해했지만, 모델 학습과정에서 가중치 초기화가 문제가 있다는것을 구체적으로\n어떻게 탐지할 수 있을까?\n사실, 몇가지 징후를 포착할수는 있지만, 문제의 원인이 꼭 초기화에 있다고 단정짓기는 쉽지않으므로,\n아래의 현상이 발생할 경우, 모델자체의 문제인지, activation function 문제인지, 데이터의 문제인지는 자세히 살펴봐야 알 수 있다.\n어쨌든 아래의 현상이 있을 경우 \u0026lsquo;가중치 초기화\u0026rsquo;의 문제를 예상되는 원인의 하나로 보고 접근해볼 수 있다.\n# 학습이 매우 느리게 진행되는 경우\n적절한 가중치 초기화가 이루어지지 않으면, 신경망은 매우 작은 그라디언트 값을 가질 수 있다.\n이는 가중치 업데이트가 충분히 이루어지지 않아 학습이 느리게 진행될 수 있음을 의미한다.\n# 학습이 전혀 진행되지 않는 경우\n가중치가 너무 크게 초기화되어 그라디언트 폭발이 발생하거나, 너무 작게 초기화되어 그라디언트 소실이 발생할 수 있습니다.\n이는 손실 함수의 값이 줄어들지 않고 변동성이 크거나, 전혀 개선되지 않는 현상으로 나타날 수 있다.\n# 손실 함수의 값이 NaN이나 무한대로 발산하는 경우\n가중치가 너무 크게 초기화되어 오버플로를 일으키거나, 계산 중에 불안정한 수치 문제가 발생할 수 있다.\n이는 학습 과정에서 손실 함수의 값이 NaN(Not a Number)이나 무한대로 발산하는 현상으로 관찰될 수 있다.\n# 검증 성능이 개선되지 않거나 과적합이 심해지는 경우\n가중치 초기화가 모델의 학습 능력에 부적절하게 영향을 미쳐, 학습 데이터에는 빠르게 적응하지만 검증 데이터셋에서는 성능이 개선되지 않거나, 과적합이 발생할 수 있다.\n# Reference # https://blog.naver.com/handuelly/221831940317\nhttps://lv99.tistory.com/23\n","date":"2024.02.17","externalUrl":null,"permalink":"/posts/85/","section":"글","summary":"","title":"Weight initialization(He, Xavier, LeCun)","type":"posts"},{"content":"","date":"2024.02.17","externalUrl":null,"permalink":"/tags/xavier/","section":"Tags","summary":"","title":"Xavier","type":"tags"},{"content":"","date":"2024.02.17","externalUrl":null,"permalink":"/tags/%EA%B0%80%EC%A4%91%EC%B9%98-%EC%B4%88%EA%B8%B0%ED%99%94/","section":"Tags","summary":"","title":"가중치 초기화","type":"tags"},{"content":"","date":"2024.02.17","externalUrl":null,"permalink":"/tags/%ED%99%9C%EC%84%B1%ED%95%A8%EC%88%98/","section":"Tags","summary":"","title":"활성함수","type":"tags"},{"content":"","date":"2024.02.07","externalUrl":null,"permalink":"/tags/autograd/","section":"Tags","summary":"","title":"Autograd","type":"tags"},{"content":"","date":"2024.02.07","externalUrl":null,"permalink":"/tags/backpropagation/","section":"Tags","summary":"","title":"Backpropagation","type":"tags"},{"content":"","date":"2024.02.07","externalUrl":null,"permalink":"/tags/grad/","section":"Tags","summary":"","title":"Grad","type":"tags"},{"content":"","date":"2024.02.07","externalUrl":null,"permalink":"/tags/grad_fn/","section":"Tags","summary":"","title":"Grad_fn","type":"tags"},{"content":"","date":"2024.02.07","externalUrl":null,"permalink":"/tags/pytorch/","section":"Tags","summary":"","title":"Pytorch","type":"tags"},{"content":"우리는 보통 딥러닝 모델을 학습할때, Pytorch, TensorFlow, Keras와 같은 딥러닝 라이브러리를 사용한다.\n딥러닝 라이브러리들을 사용할때의 장점은 아래의 몇가지로 요약해볼 수 있다.\n복잡한 수학적 연산을 간편한 API 구조 뒤에 숨김으로써, 딥러닝 모델의 학습 및 개발 과정을 단순화 해준다.\n모델 파라미터를 업데이트하기 위한 미분과정에 대한 자동화 기능을 제공한다.\nGPU를 사용한 데이터의 batch 처리, 멀티스레딩, 데이터 로딩등의 기능을 지원한다.\n그 중에서도 딥러닝 라이브러리를 사용하는 가장 주된 이유는 2번에 해당하는 \u0026lsquo;AutoGrad\u0026rsquo; 기능일 것이다.\n먼저, 딥러닝 모델이 데이터를 통해 학습을 진행하는 과정을 간단하게 요약하면,\n먼저 예측값을 도출하고, 예측값과 정답값의 차이를 통해 loss를 구한다. 그리고, loss에 영향을 미친 parameter에 대하여, parameter가 loss에 영향을 미친 정도에 대해 편미분값을 통해 gradient를 계산한다. 계산된 gradient를 통해 모델(네트워크) parameter의 weight와 bias를 업데이트하며 학습을 진행한다. 와 같은 과정, 즉 backpropagation 과정을 거친다.\n그리고 딥러닝 라이브러리는 \u0026lsquo;AutoGrad\u0026rsquo; 기능을 통해 위 계산 과정을 자동으로 수행하는 기능을 제공한다.\n오늘은 딥러닝 라이브러리 중 pytorch 라이브러리에서 제공하는 AutoGrad 기능이, 구체적으로 어떤 과정을 통해\nbackpropagation을 수행하는지 자세히 살펴보도록 하자.\n먼저, AutoGrad의 개념을 한번 더 정리해보자.\n→\u0026lsquo;AutoGrad\u0026rsquo; : # 1) *variable연산을 통해 생성 된 계산 그래프를 사용하여, 변수들의 그래디언트를 자동으로 계산하는\nPytorch의 기능을 의미한다.\n2) *variable 연산 과정을 바탕으로, 역전파를 자동으로 수행하여, 각 변수의 그래디언트를 계산한다.\n위 설명을 보면, **\u0026lsquo;variable 연산\u0026rsquo;**이라는 과정이 결국 autograd를 수행하는데 핵심적인 역할을 하게되는데,\nvariable은 다음과 같은 기능을 담당한다.\n→ \u0026lsquo;Variable 연산\u0026rsquo; : # 1) Pytorch에서 변수들간의 연산을 의미하며, 이는 자동으로 계산 그래프를 구성하여 나중에 그래디언트를 계산할 수 있게 한다.\n2) 사용자가 직접 그래디언트를 계산하기 위한 수식을 정의할 필요없이 연산과정을 자동으로 추척하고 저장한다는 특징을 가진다.\n* 참고\n과거 pytorch에서는 연산과정을 기록하기 위해, 아래와 같이 variable클래스로 tensor를 감싸(wrapping)미분이 가능한 형태로 만들었어야 했으나, 최근 Pytorch에서는 tensor와 variable개념이 통합되어, 모든 tensor는 variable클래스로 감싸지 않고도, 자동 미분을 지원하기때문에, requires_grad() 속성을 사용해 그라디언트 계산 여부를 설정할 수 있다. 다만, 해당 포스팅에서는 자동 미분을 할 수 있는 연산과정을 자동으로 추적하고 저장하는 과정을 variable이라는 개념으로 설명하도록 하겠다.\nAutoGrad에 대해 어느정도 개념적인 이해가 되었다면, 이제 실제로 pytorch 코드내에서 자동 그래디언트 계산과정이\n어떻게 진행되는지 예시를 통해 살펴보자.\n1) 최초 변수 선언 # 먼저, 2x2의 1만 있는 행렬을 Tensor를 선언해주도록 하고, requires_grad옵션을 True로 설정하여,\n해당 변수는 gradient 계산이 필요한 변수로 설정한다.\n만약, requires_grad를 \bFalse로 설정한 경우, 이후에 설명하겠지만 grad_fn이라는 메서드에 gradient를 계산하기 위한\n함수(연산기록)가 기록(저장)되지않았으므로, gradient계산이 불가하다는 *오류가 발생한다.\nRuntimeError: element 0 of tensors does not require grad and does not have a grad_fn 즉, requires_grad=True로 설정한다는 것은 \u0026ldquo;해당 변수는 gradient계산이 필요하며, gradient를 계산하기 위해 이후 연산과정에 대해 그래디언트를 계산할 수 있는 함수들을 저장하겠다.\u0026rdquo; 라는 의미를 가지는 것이다.\n해당 변수에 대한 gradient 함수의 추적/기록 설정\n2) 내부 메서드 : data, grad, grad_fn # 자, 그렇다면 위에서 언급한 gradient계산을 위한 정보들은 어디에서 기록되고 추적되는걸까?\npytorch에서는 \u0026lsquo;requires_grad=True\u0026rsquo;로 설정된 tensor의 경우 아래와 같은 메서드를 통해 그래디언트 연산 결과값과 그래디언트를 계산할 수 있는 함수정보를 기록한다\n1) data (현재는 굳이 사용할 필요 없는 메서드로, 변수명 호출을 통해 데이터 확인가능 함. 참고만 할 것)\nTensor 형태의 실제 변수 데이터가 담기고, 이를 확인할 수 있는 메서드이다. 다만, 최신 Tensor에서는 variable이 tensor로 대체된 관계로, data 속성으로 굳이 원본 데이터를 확인할 필요없이\n변수명 그대로 호출 하여 확인할 수 있다.\n2) grad\ndata가 거쳐온 layer의 미분값이 축적되고, 확인할 수 있는 매서드이다.\nbackward()함수가 호출 될 때, Pytorch는 tensor의 그래디언트를 자동으로 계산하고, 이를 grad속성에 할당한다. weight 및 bias의 update를 위한 미분 계산값, 즉, gradient desent value가 여기에 들어가게 된다.\n3) grad_fn\n해당 Tensor를 생성한 연산을 나타내며,미분값을 계산한 함수에 대한 정보가 축적된다.\n즉, \u0026lsquo;어떤 연산(함수)에 대한 backward를 진행했다\u0026rsquo;라는 정보가 들어가게 되는데,\nPytorch의 모든 연산(Operation)이, 결과 텐서에 대해 grad_fn 을 가지고 있으며, 이를 통해 그래디언트 계산 시\n역전파를 위한 연산의 체인을 구성하게 된다.\n사용자가 직접 생성한 텐서는 아래 예시와 같이 grad_fn이 None으로 출력된다.\ndata, grad, grad_fn 출력 예시\n# 3) Tensor 연산 # 위 Tensor연산을 살펴보면,\n먼저, 첫번째 연산에서는 텐서에 특정 상수를 더하는 연산을, 두번째는 제곱, 세번째는 합계 연산을 수행했다.\n그리고, 각 메서드를 확인해보면 grad 메서드에는 아직 gradient계산이 진행되지않았기때문에 None으로\n출력되는것을 확인할 수 있다.\n그리고, grad_fn에는 아래와 같은 내용이 출력되는데,\n\u0026lt;AddBackward0 object at 0x7fe6809d4dc0\u0026gt; 앞의 ***\u0026lsquo;AddBackward0\u0026rsquo;***은 해당 tensor가 수행한 **\u0026lsquo;연산의 종류\u0026rsquo;**를 나타내며, 뒤의 ***\u0026lsquo;0x7fe6809d4dc0\u0026rsquo;***은\n고유식별자 정보를 나타낸다.\n고유식별자 정보는 해당 텐서를 생성한 연산을 가리키고, 이 연산 정보를 통해 pytorch는 *계산그래프를 역순으로\n거슬러올라가면서, 그래디언트를 계산할 수 있다.(backward과정)\n* 계산그래프 (중요): 계산그래프는 엣지와 노드로 구성되어 있으며, 노드는 텐서(데이터)를, 엣지는 텐서간의 연산정보 (예를들어 덧셈, 곱셈, 지수연산 등)를 나타낸다. \u0026lsquo;backward()\u0026lsquo;가 호출되면, 계산 그래프는 마지막 연산(일반적으로 손실함수에서 시작함)부터 시작해서, 각 연산의 그래디언트를 계산하고, 이를 통해 입력텐서의 그래디언트를 업데이트하게 된다. 그리고 이 과정을 chain rule로 계산하게된다.\n4) AutoGrad 연산 및 Backpropagation # 우리가 보통 model의 output과 target사이에서 구한 loss값에 대해 \u0026rsquo;loss.backward()\u0026lsquo;를 수행하듯이,\n마지막 연산의 결과값에 대해 backward()를 진행하게 되면, 계산그래프를 통해 각 연산과정에 대해 역순으로\n거슬러올라가며 미분값을 계산하게 된다.\n그리고, 최초 변수의 grad 메서드에서 계산된 gradient값을 확인할 수 있다.\n여기서 잠깐 주목할 부분이 있다.\n최종 backward()직전에, \u0026lsquo;out.sum()\u0026rsquo; 작업이 있는데,\n최종 tensor에 대해 sum()을 하는 이유는 backward()가 기본적으로 스칼라값에 대한 그래디언트를 계산하기 때문이다.\n즉, \u0026lsquo;backward()\u0026lsquo;가 스칼라 값에 대해서만 호출될 수 있는 이유는 그래디언트 계산이 스칼라 값에 대한 미분으로 정의되기 때문이다.\n이는 딥러닝에서 손실함수(loss func)의 값을 최소화하는것과 직접적으로 관련이 있고, 손실함수는 일반적으로 스칼라 값이다!\n다시 코드로 돌아가서, \u0026lsquo;out=e.sum()\u0026lsquo;은 \u0026rsquo;e\u0026rsquo;텐서의 모든 요소를 더하여 스칼라값을 생성한다.\n그리고, 이 스칼라에 대해 \u0026lsquo;backward()\u0026lsquo;함수를 호출함으로써 해당 스칼라 값이 \u0026lsquo;a\u0026rsquo;, \u0026lsquo;b\u0026rsquo;, \u0026lsquo;c\u0026rsquo;등의 입력 텐서에 대해 어떻게 변화하는지에\n대한 그래디언트(변화율)를 계산할 수 있게된다.\n따라서, backward()함수내부에서 연쇄법칙(chain Rule)을 사용하여, 주어진 스칼라 값(out)에 대한 입력 텐서들(a, b, c)의 미분을 계산한다. 만약, \u0026lsquo;backward()\u0026lsquo;에 인자가 주어지지 않는다면, 이는 backward()가 호출된 스칼라값에 대한 그래디언트가 1로 가정된다는것을 의미한다. 즉, out.backward()는 \u0026lsquo;out\u0026rsquo;이라는 스칼라값에 대해 \u0026lsquo;out.backward(torch.tensor(1.))\u0026lsquo;을 호출한것과 같으며, 이는 \u0026lsquo;out\u0026rsquo;을\n생성하는데 기여한 모~~든 텐서에 대한 그래디언트를 계산하라는 명령과 같다.\n자, 이제 오늘 알아본 위 AutoGrad 과정을 간단하게 정리해보면,\n텐서를 선언할때, requires_grad=True로 옵션을 선택하여 연산과정을 자동으로 추적하도록 설정한다. 해당 tensor에 대해서 연산을 진행할 경우, grad_fn 메서드에 연산의 종류정보와 결과값을 얻은 연산의\n그래프 고유식별자 정보를 기록한다. backward() 호출 시, 계산그래프를 통해 변수간의 연산을 단계별로 추적하며, 미분을 계산한다. 최초 변수의 grad메서드에 최종 계산된 그래디언트 값을 확인할 수 있다. 마치며 # 이번 AutoGrad 정리를 통해,\n그 동안 딥러닝 모델 학습 코드를 작성하면서, 기계적으로 작성했던 Auto backpropagation 과정에 대해 조금 더 깊게 이해할 수 있었다.\nPytorch에서는 AutoGrad외에도 딥러닝 모델 개발에 있어, 다양한 편의 기능들을 제공하고있는데,\n앞으로도 실제 모델 개발시에 유용하게 활용할 수 있는 다양한 기능들에 대해 하나하나 공부하고 소개할 수 있도록 해야겠다.\nReference # https://www.youtube.com/watch?v=E0R9Xf_GyUc\nhttps://9bow.github.io/PyTorch-tutorials-kr-0.3.1/beginner/examples_autograd/two_layer_net_autograd.html\n[PyTorch: Variable과 autograd — PyTorch Tutorials 0.3.1 documentation\nPyTorch: Variable과 autograd 하나의 은닉 계층(Hidden Layer)과 편향(Bias)이 없는 완전히 연결된 ReLU 신경망에 유클리드 거리(Euclidean Distance)의 제곱을 최소화하여 x로부터 y를 예측하도록 학습하겠습니다.\n9bow.github.io](https://9bow.github.io/PyTorch-tutorials-kr-0.3.1/beginner/examples_autograd/two_layer_net_autograd.html)\nhttps://www.youtube.com/watch?v=TxIVr-nk1so\u0026t=0s ","date":"2024.02.07","externalUrl":null,"permalink":"/posts/84/","section":"글","summary":"","title":"Pytorch의 'AutoGrad' 과정을 하나하나 뜯어보자!","type":"posts"},{"content":"","date":"2024.02.07","externalUrl":null,"permalink":"/tags/variable/","section":"Tags","summary":"","title":"Variable","type":"tags"},{"content":"","date":"2024.02.07","externalUrl":null,"permalink":"/tags/%EC%9E%90%EB%8F%99%EB%AF%B8%EB%B6%84/","section":"Tags","summary":"","title":"자동미분","type":"tags"},{"content":"","date":"2024.02.07","externalUrl":null,"permalink":"/tags/decorator/","section":"Tags","summary":"","title":"Decorator","type":"tags"},{"content":"","date":"2024.02.07","externalUrl":null,"permalink":"/tags/python/","section":"Tags","summary":"","title":"Python","type":"tags"},{"content":"","date":"2024.02.07","externalUrl":null,"permalink":"/tags/python-decorator/","section":"Tags","summary":"","title":"Python Decorator","type":"tags"},{"content":"오늘은 Python의 데코레이터를 사용하는 방법에 대해 정리해보자.\n@decorator_ # \u0026lt;-- 요런거 def function(): print \u0026#34;what is decorator?\u0026#34; Github등 오픈 소스 커뮤니티에 공유된 코드들을 읽다보면, 위와 같이 \u0026lsquo;@\u0026rsquo; 기호로 표시된 코드들을 자주 볼 수 있다.\n오늘 알아보려하는 데코레이터는 보통 이 \u0026lsquo;@\u0026lsquo;기호로 정의된 함수 또는 클래스를 의미한다.\n데코레이터를 간단히 설명하면,\n\u0026ldquo;다른 함수를 감싸는 함수\u0026quot;로, 감싸진 함수에 추가적인 기능을 제공하거나, 감싸진 함수의 동작을 변경하는데 사용되는 기능이다.\nBut, 위 설명만 가지고는 사실 잘 이해가 되지않는다.\n우선은 **\u0026lsquo;다른 함수를 감싸는 함수\u0026rsquo;**라는 부분만 기억하고 넘어가자.\n# 데코레이터\u0026hellip;? 어떻게 사용하는거지? # 데코레이터는 함수 형태로 사용하는 방법과 클래스로 사용하는 방법이 있다.\n먼저, 함수로 데코레이터를 사용하는 방법에 대해 먼저 살펴보자.\n1. 함수형태로 사용하는 방법 # 위에서 데코레이터는 함수를 감싸는 함수로써, 감싸진 함수에 추가적인 기능을 제공하거나, 동작을 변경하는데 사용된다고했다.\n그 예시로 아래 코드를 살펴보자.\nimport time def timer_decorator(func): def wrappers(*arg, **kwargs): start_time = time.time() result = func(*arg, **kwargs) end_time = time.time() print(f\u0026#34;{func.__name__} 실행 시간: {end_time - start_time}초\u0026#34;) return result return wrappers @timer_decorator def example_function(): for _ in range(1000000): pass example_function() \u0026#34;\u0026#34;\u0026#34; [OUTPUT] example_function 실행 시간: 0.04586601257324219초 \u0026#34;\u0026#34;\u0026#34; 위 코드는 간단한 roof 함수를 데코레이터로 wrapping한 예시 코드인데, 위 코드에서 데코레이터가 동작하는 순서를 아래에 정리해보자.\n(1) 가장 먼저, \u0026rsquo;time_decorator\u0026rsquo;이라는 데코레이터 함수를 생성한다.\n함수의 내용은, 인자로 받은 \u0026lsquo;func\u0026rsquo;함수를 실행하고, 함수 실행 이전과 이후에 시간을 체크하여, 함수의 실행시간을 측정하여 출력하는\ntimer함수이다.\n(2) 그 다음, 만들어진 데코레이터 함수를 통해 \u0026lsquo;감쌀 함수\u0026rsquo;로 \u0026rsquo;example_function\u0026rsquo;이라는 함수를 정의했다.\n해당 함수는 100만번의 루프를 도는 간단한 함수이다.\n(3) 그리고 해당 함수를 \u0026lsquo;@\u0026lsquo;를 통해 \u0026lsquo;@timer_decorator\u0026rsquo;함수로 대상 함수를 감싸주면 데코레이터 적용이 완료된다.\n이렇게 함수를 구성하게 되면, 아래에서 \u0026rsquo;example_function()\u0026lsquo;을 통해 감싸진 함수를 호출할때,\n해당 함수는 \u0026lsquo;@timer_decorator\u0026rsquo;이라는 데코레이션 함수로 wrapping된 상태이기 때문에,\n데코레이터 함수 \u0026rsquo;timer_decorator\u0026rsquo; 의 인자로 example_function함수가 들어가게 되고, 데코레이터 함수가 실행된다.\n그리고, 데코레이터 함수내에서는, 인자로 받은 \u0026rsquo;example_fuanction\u0026rsquo;함수가 실행되고, 해당 함수의 실행시간이 측정되어\n시간이 출력된다.\n즉, 함수 실행은 \u0026rsquo;example_function()\u0026lsquo;로 호출했지만,\n해당 함수가 decorator로 wrapping되어있었기 때문에,\nwrapping된 데코레이터 함수 내부에서 인자로 받은 \u0026rsquo;example_function\u0026rsquo;함수를 실행한 결과값을 반환하게 된 것이다.\n이해가 되는가..?\n여기서 데코레이터 함수를 구성할때, 주의해야할 점이 있다.\nimport datetime def time_check(func): print(datetime.datetime.now()) func() print(datetime.datetime.now()) @time_check def output_func(): print(\u0026#39;이거 위아래로 시간을 찍어라\u0026#39;) output_func() \u0026#34;\u0026#34;\u0026#34; [OUTPUT] 2024-02-07 11:57:21.361220 이거 위아래로 시간을 찍어라 2024-02-07 11:57:21.362256 --------------------------------------------------------------------------- TypeError Traceback (most recent call last) /var/folders/2g/2b3fqv816cb5kxrlc3v553280000gn/T/ipykernel_10484/1157037113.py in \u0026lt;module\u0026gt; 10 print(\u0026#39;이거 위아래로 시간을 찍어라\u0026#39;) 11 ---\u0026gt; 12 output_func() TypeError: \u0026#39;NoneType\u0026#39; object is not callable \u0026#34;\u0026#34;\u0026#34; 위 데코레이터 함수는 언뜻 봤을때는 큰 문제가 없어보이지만, 오류가 발생한다.\n오류와 더불어서 동작에도 문제가 있는데, \u0026lsquo;output_func()\u0026lsquo;와 같이 output_func함수가 호출되는 시점에서,\n데코레이터를 포함한 output_func 함수가 실행되어야 하지만,\n위 출력된 결과값을 보면,\n\u0026lsquo;output_func()\u0026lsquo;가 호출되기 전에 이미,\n2024-02-07 11:57:21.361220 이거 위아래로 시간을 찍어라 2024-02-07 11:57:21.362256 함수의 결과값이 출력되었고, 이후에 output_func() 호출과정에서 오류가 발생한 것을 확인할 수 있다.\n이 오류와 관련해서 우리가 데코레이터를 사용함에 있어, 기억해야할 것은\n데코레이터함수 내부에서 wrapping된 함수를 불러와 실행할때는 데코레이터 내부에 또다른 함수(wrapper), 즉 위에서 설명한 올바른 코드의 예시에서 \u0026lsquo;wrappers\u0026rsquo;함수와 같은 데코레이터 함수 내에, wrapper함수를 정의해서, 인자로 받은 외부 함수를 감싸야 한다는 것이다.\n반드시, 데코레이터 함수 내부\u001d의 wrapper함수로 외부함수를 감싸야지만, \u0026lsquo;output_func\u0026rsquo;함수가\n\u0026lsquo;호출되는 시점\u0026rsquo;에 실행되는, 올바른 동작 형태를 가지게 된다.\n이 부분은 데코레이터 함수를 사용하는 규칙이므로 사용방식을 이해하고 넘어가면 된다.\n동작방식은 ok\u0026hellip; 그래서, 데코레이터를 왜 사용하는거지? # def example_function1(): start_time = time.time() for _ in range(5000000): pass end_time = time.time() print(f\u0026#34;example_function1 실행 시간: {end_time - start_time}초\u0026#34;) def example_function2(): start_time = time.time() for _ in range(5000000): pass end_time = time.time() print(f\u0026#34;example_function2 실행 시간: {end_time - start_time}초\u0026#34;) def example_function3(): start_time = time.time() for _ in range(5000000): pass end_time = time.time() print(f\u0026#34;example_function3 실행 시간: {end_time - start_time}초\u0026#34;) example_function1() example_function2() example_function3() \u0026#34;\u0026#34;\u0026#34; example_function1 실행 시간: 0.10071301460266113초 example_function2 실행 시간: 0.10005807876586914초 example_function3 실행 시간: 0.10119986534118652초 \u0026#34;\u0026#34;\u0026#34; 만약 데코레이터를 사용하지 않고 위의 example_function1~3함수 각각 실행시간을 체크하려고 한다면,\n아마도 위의 코드와 같이 각 함수별로 timer 코드를 중복으로 넣어줘야할것이다.\n하지만, 데코레이터를 사용하면 각 함수에 중복으로 활용되는 코드를 반복적으로 입력할 필요없이,\n별도로 정의된 데코레이터 함수를 통해 간결하고 가독성 좋은 코드를 작성할 수 있다.\n아래 코드에서 데코레이터 활용시의 장점을 확인할 수 있다.\ndef timer_decorator(func): def wrapper(*arg, **kwargs): start_time = time.time() result = func(*arg, **kwargs) end_time = time.time() print(f\u0026#34;{func.__name__} 실행 시간: {end_time - start_time}초\u0026#34;) return result return wrapper @timer_decorator def example_function1(): for _ in range(1000000): pass @timer_decorator def example_function2(): for _ in range(10000000): pass @timer_decorator def example_function3(): for _ in range(5000000): pass example_function1() example_function2() example_function3() \u0026#34;\u0026#34;\u0026#34; [OUTPUT] example_function1 실행 시간: 0.02248668670654297초 example_function2 실행 시간: 0.2073652744293213초 example_function3 실행 시간: 0.10090112686157227초 \u0026#34;\u0026#34;\u0026#34; 2. 클래스 형태로 사용하는 방법 # 함수로 데코레이터를 사용하는 방법을 이해했다면, 클래스로 사용하는 방법은 금방 이해할 수 있다.\n클래스 데코레이터는,\n별도의 데코레이터 클래스를 만든다.\n데코레이터 클래스 내부의 __call__ 매서드 내부에, 데코레이터를 통해 실행하고자하는 코드를 넣어 정의한다.\n나머지는 다 똑같다!\n아래 코드에서 확인해보자.\nclass Timer_Decorator: def __init__(self, func): self.func = func def __call__(self, *arg, **kwargs): start_time = time.time() self.func(*arg, **kwargs) end_time = time.time() print(f\u0026#39;{self.func.__name__} 실행시간: {end_time-start_time}초\u0026#39;) class MainClass: @Timer_Decorator def example_function1(): for _ in range(1000000): pass @Timer_Decorator def example_function2(): for _ in range(10000000): pass @Timer_Decorator def example_function3(): for _ in range(5000000): pass this_class = MainClass() this_class.example_function1() this_class.example_function2() this_class.example_function3() \u0026#34;\u0026#34;\u0026#34; [OUTPUT] example_function1 실행시간: 0.029875993728637695초 example_function2 실행시간: 0.19496917724609375초 example_function3 실행시간: 0.09695100784301758초 \u0026#34;\u0026#34;\u0026#34; 설명한것과 같이 데코레이터 클래스의 __call__매서드 내부에, 데코레이터를 실행할 때 호출할 코드내용을 담는다.\n데코레이터는 클래스명으로 정의하면 된다.\n위 코드와 같이, 외부 클래스 내부에 속한 내장함수들의 위에 데코레이터(클래스)를 붙여주고,\n각각의 내장함수를 호출하면, wrapping된 데코레이터의 내용을 타고 출력물이 생성되는것을 확인할 수 있다.\n위에서는 클래스 내부의 내장함수에 데코레이터를 붙이는 예시로 들었지만,\n클래스로 생성한 데코레이터를 일반 함수에 바로 붙여도, 아래와 같이 동일하게 잘 동작한다.\nclass Timer_Decorator: def __init__(self, func): self.func = func def __call__(self, *arg, **kwargs): start_time = time.time() self.func(*arg, **kwargs) end_time = time.time() print(f\u0026#39;{self.func.__name__} 실행시간: {end_time-start_time}초\u0026#39;) @Timer_Decorator def example_function1(): for _ in range(1000000): pass @Timer_Decorator def example_function2(): for _ in range(10000000): pass @Timer_Decorator def example_function3(): for _ in range(5000000): pass this_class = MainClass() example_function1() example_function2() example_function3() \u0026#34;\u0026#34;\u0026#34; [OUTPUT] example_function1 실행시간: 0.03767681121826172초 example_function2 실행시간: 0.20968103408813477초 example_function3 실행시간: 0.09826517105102539초 \u0026#34;\u0026#34;\u0026#34; 마치며 # 오늘은 파이썬 데코레이터에 대해서 알아보았다.\n데코레이터는 잘만 사용하면 반복적으로 작성해야하는 코드들을 줄여주기때문에, 코드의 간결성을 높일 수 있고,\n기능별로 정의하여 함수 및 클래스에 이식하여 사용할 수 있기 때문에\n코드의 가독성을 높이고, 객체지향적 코드를 구현하는데도 좋은 기능이라고 생각된다.\n다만, 데코레이터에는 별도의 추가 인자를 받아 처리하는 방법 등 생각보다 매우 다양한 방식\u001d의 활용 케이스들이 있다.\n따라서, 위 기본적인 내용을 바탕으로 좀 더 여러 케이스들을 접하고 활용해보는 공부가 필요할 것 같다.\nReference # https://bluese05.tistory.com/30 [python decorator (데코레이터) 어렵지 않아요\nPython decorator (데코레이터) Python 으로 작성된 Opensource 의 코드들을 보다 보면, 아래와 같이 @ 로 시작하는 구문 들을 볼 수 있다. @decorator_def function(): print \u0026ldquo;what is decorator?\u0026rdquo; decorator를 한마디로 얘기\nbluese05.tistory.com](https://bluese05.tistory.com/30)\n","date":"2024.02.07","externalUrl":null,"permalink":"/posts/83/","section":"글","summary":"","title":"Python Decorator @ 사용방법 완벽 이해하기!","type":"posts"},{"content":"","date":"2024.02.07","externalUrl":null,"permalink":"/tags/%EB%8D%B0%EC%BD%94%EB%A0%88%EC%9D%B4%ED%84%B0/","section":"Tags","summary":"","title":"데코레이터","type":"tags"},{"content":"","date":"2024.02.06","externalUrl":null,"permalink":"/tags/generator/","section":"Tags","summary":"","title":"Generator","type":"tags"},{"content":"","date":"2024.02.06","externalUrl":null,"permalink":"/tags/iteration/","section":"Tags","summary":"","title":"Iteration","type":"tags"},{"content":"오늘은 파이썬의 generator 개념을 한번 정리해보도록 하겠다.\nPython Generator란? # 먼저 파이썬에서 generator란, 반복자(iterator)와 유사한 개념으로, 한번에 **‘하나’**의 항목을 생성하는 객체를 의미한다.\n일반함수와는 다르게, **‘yield’**문을 사용해서 값을 반환하는데, 하나의 값을 반환 후 함수의 실행상태를 유지하며, 필요할때마다 값을 생성할 수 있는데, 이전에 생성한 다음부터 이어서\n생성하는 특징이 있다.\n먼저, yield문을 활용한 간단한 파이썬 제너레이터 예시를 살펴 보자.\ndef simple_generator(): yield 1 yield 2 yield 3 # 제너레이터 생성 gen = simple_generator() # 제너레이터에서 값을 하나씩 가져오기 print(next(gen)) # 1 print(next(gen)) # 2 print(next(gen)) # 3 위 예시를 보면 먼저 ‘simple_generator’함수는 제너레이터를 정의하고있다. 이 함수는 ‘yield’문을 통해 세개의 숫자를 순차적으로 생성하는데,\n**‘next()’**함수를 호출할때마다, 제너레이터는 이전 출력값의 ‘다음’ 출력값을 반환하고, 반환 즉시, 그 위치에서 실행을 일시중지한다.\n마찬가지로, 다음 ‘next()’ 호출시에는 일시 중지된 위치부터 다시 실행을 시작한다.\n원리와 동작은 좀 이해가 가는가?\n그럼, 이제 제너레이터는 왜 사용하고, 어떤 케이스에서 활용되는지 궁금하다.\n아래에서 파이썬 제너레이터가 사용되는 몇가지 예시를 살펴보자.\n# CASE 1) 데이터 스트리밍 및 파일처리 # 먼저, 제너레이터의 가장 큰 특징인, 한번에 하나의 출력만 수행한다는 점을 활용해서,\n큰 데이터셋을 처리할떄, 전체 데이터를 한번에 메모리에 로드하는 대신, 필요한 부분만 순차적으로 처리할 수 있다.\n아래 예시코드를 보자.\ndef read_large_file(file_name): with open(file_name, \u0026#39;r\u0026#39;) as file: for line in file: yield line # 파일에서 한 줄씩 읽기 for line in read_large_file(\u0026#39;large_file.txt\u0026#39;): print(line) 위 코드에서 ‘read_large_file’함수에서는 로드한 파일에 대해, ‘yield’문을 통해 한줄씩 출력한다.\n덕분에 한번에 데이터를 로드하지않고, 필요할때마다 한줄씩 꺼내서 쓸 수 있다.\n비슷한 예를 한가지 더 살펴보자.\ndef read_file_in_chunks(file_name, chunk_size=1024): with open(file_name, \u0026#39;r\u0026#39;) as file: while True: chunk = file.read(chunk_size) if not chunk: break yield chunk # 파일을 청크 단위로 읽기 for chunk in read_file_in_chunks(\u0026#39;large_file.txt\u0026#39;): print(chunk) 비슷하게, 파일을 chunk단위로 읽어서, 필요한 만큼 반복적으로 불러올 수 있다.\n예를들어, 대용량의 로그파일을 읽어와 특정 키워드를 체크해야한다고 했을때, 전체파일을 읽어와 분석한다면 메모리가 크게 사용되어 비효율적이다.\n제너레이터를 사용하면, 한번에 하나의 line씩 불러와 진행할 수 있기때문에, 메모리 사용을 최적화 하는데 유용하다.\nCASE 2) 무한 시퀀스 생성 # 무한으로 시퀀스를 생성하는 함수에서 yield문을 통해, 한번에 하나의 시퀀스만 출력하도록 구성하면,\n필요한 시점에 이전 출력 이후의 시퀀스를 계속해서 출력받아 사용할 수 있다.\ndef fibonacci(): a, b = 0, 1 while True: yield a a, b = b, a + b # 피보나치 수열의 처음 10개 숫자 출력 fib = fibonacci() for _ in range(10): print(next(fib)) 위는 피보나치수열을 생성하는 간단한 함수이다. 이 함수를 통해 생성되는 피보나치 수열의 원소들을 순차적으로 필요한 시점에, 사용자가 정한 만큼 계속 출력해낼 수 있다.\n아마도 데이터가 적재되는 시점에, 이에 대한 unique한 시퀀스가 필요할때 활용될 수 있는 방법이지 않을까 생각해본다.\nCASE 3) 비동기 프로그래밍 # # 비동기 프로그래밍에는 asyncio 라이브러리와 같은 추가적인 도구가 필요하며, # 제너레이터를 사용하여 비동기 작업을 관리할 수 있다. # 예시: async def async_generator(): for i in range(10): await asyncio.sleep(1) yield i 마지막으로 비동기 프로그래밍에서도 제너레이터가 유용하게 사용될 수 있는데,\n‘yield’문을 통해, 비동기 함수내에서, 함수 내 정의된 기능을 필요한 시점에 계속해서 출력받을 수 있도록 활용할 수 있다.\n# 마치며 # 오늘 알아본 python 제너레이터를 요약해보면,\n사용자가 \u0026ldquo;**원하는 시점에 연속적으로 출력받을 수 있는 생성자의 기능\u0026rdquo;**으로 정의할 수 있을 것 같다.\niteration에서 한번에 하나의 출력만 하며, 출력시점을 사용자가 선택할 수 있다는 점에서 특히, 대용량의 파일 관리시에 유용하게 활용될 수 있는 기능으로 보여진다.\n# Reference # https://wikidocs.net/16069 [39. Generator(제네레이터)\n1. Generator란? - generator : iterator를 생성해주는 함수, 함수안에 yield 키워드를 사용함 - genrator 특징 - iterab… # wikidocs.net](https://wikidocs.net/16069)\n","date":"2024.02.06","externalUrl":null,"permalink":"/posts/82/","section":"글","summary":"","title":"Python Generator (a.k.a. 'yield')","type":"posts"},{"content":" As-Is # : 데이터프레임에서 특정 컬럼들의 값을 for문을 통해 불러와, 특정 key(patient_code, seq_no)의 유무를 체크해 dictionary내의 값을 채움\n# 조회용 Dict 생성 search_col = [\u0026#39;patient_code\u0026#39;, \u0026#39;seq_no\u0026#39;, \u0026#39;disease_code\u0026#39;, \u0026#39;atc_set\u0026#39;, \u0026#39;prescription_date\u0026#39;, \u0026#39;diagnosis_date\u0026#39;, \u0026#39;sex\u0026#39;, \u0026#39;age\u0026#39;, \u0026#39;weight\u0026#39;, \u0026#39;bmi\u0026#39;, \u0026#39;a1c\u0026#39;, \u0026#39;scr\u0026#39;, \u0026#39;egfr\u0026#39;] transform_dict = {} for patient_code, seq_no, disease_code, atc_set, prescription_date, diagnosis_date, sex, age, weight, bmi, a1c, scr, egfr in tqdm(data[search_col].values): if patient_code not in transform_dict: transform_dict[patient_code] = { seq_no : { \u0026#39;disease_code\u0026#39; : disease_code, \u0026#39;atc_set\u0026#39; : atc_set, \u0026#39;prescription_date\u0026#39; : prescription_date, \u0026#39;diagnosis_date\u0026#39; : diagnosis_date, \u0026#39;sex\u0026#39; : sex, \u0026#39;age\u0026#39; : age, \u0026#39;weight\u0026#39; : weight, \u0026#39;bmi\u0026#39; : bmi, \u0026#39;a1c\u0026#39; : a1c, \u0026#39;scr\u0026#39; : scr, \u0026#39;egfr\u0026#39; : egfr } } else : transform_dict[patient_code][seq_no] = { \u0026#39;disease_code\u0026#39; : disease_code, \u0026#39;atc_set\u0026#39; : atc_set, \u0026#39;prescription_date\u0026#39; : prescription_date, \u0026#39;diagnosis_date\u0026#39; : diagnosis_date, \u0026#39;sex\u0026#39; : sex, \u0026#39;age\u0026#39; : age, \u0026#39;weight\u0026#39; : weight, \u0026#39;bmi\u0026#39; : bmi, \u0026#39;a1c\u0026#39; : a1c, \u0026#39;scr\u0026#39; : scr, \u0026#39;egfr\u0026#39; : egfr } 문제점 # dictionary내부의 값들을 채우는데, 각 항목들에 대해 반복적으로 정의하는 구문이 포함되어 있다.(코드길이가 길어짐)\n불필요한 if/else 문 존재\nfor문에서 출력되는 iteration value의 변수를 일일이 정의하고있음(필요한 것만 정의하면 될텐데..)\nTo-Be # : iteration value중 필요한 부분만 정의하고, setdefault를 통해서, dictionary의 key 존재유무에 따른 값채우기를 간결한 코드로 대체할 수 있다.\nsearch_col = [\u0026#39;patient_code\u0026#39;, \u0026#39;seq_no\u0026#39;, \u0026#39;disease_code\u0026#39;, \u0026#39;atc_set\u0026#39;, \u0026#39;prescription_date\u0026#39;, \u0026#39;diagnosis_date\u0026#39;, \u0026#39;sex\u0026#39;, \u0026#39;age\u0026#39;, \u0026#39;weight\u0026#39;, \u0026#39;bmi\u0026#39;, \u0026#39;a1c\u0026#39;, \u0026#39;scr\u0026#39;, \u0026#39;egfr\u0026#39;] transform_dict = {} for values in tqdm(data[search_col].values): patient_code, seq_no, *rest = values transform_dict.setdefault(patient_code, {})[seq_no] = dict(zip(search_col[2:], rest)) ","date":"2024.02.05","externalUrl":null,"permalink":"/posts/81/","section":"글","summary":"","title":"[REFACTORING] dictionary에 'key' 존재 유무에 따른 데이터 채우기","type":"posts"},{"content":"","date":"2024.02.05","externalUrl":null,"permalink":"/tags/refactoring/","section":"Tags","summary":"","title":"Refactoring","type":"tags"},{"content":"","date":"2024.02.05","externalUrl":null,"permalink":"/tags/setdefault/","section":"Tags","summary":"","title":"Setdefault","type":"tags"},{"content":"오늘은 L1 Norm과 L2 Norm에 대해서 구체적으로 분석해보려한다.\nNorm? # 먼저 Norm(놈, 노름)은 뭘까?\n선형대수학에서 Norm은 벡터의 크기(magnitude) 또는 길이(length)를 측정하는 방법을 의미하다.\n여기서 길이(length)는 차원(dimension)과는 다른 의미라는 것을 알자.\n예를들어 다음과 같은 열벡터가 있다고 가정해보자.\n이 벡터의 차원은 3이고, 길이가 3인것이 아니다.\n그리고, 차원이 3이라는것은 3차원공간에 아래와 같이 벡터를 그릴 수 있다는 의미이다.\n이때, 빨간선에 해당하는 벡터의 길이를 구하는것이 바로 오늘 다룰 Norm으로 수행할 수 있는것이다.\n그런데, 왜 벡터의 길이를 구해야하는것이고, 또 왜 Norm을 활용해야하는 걸까?\nNorm은 머신러닝에서 아래 과정에서 활용될 수 있는 개념이다.\nNormalization(정규화)\n: Norm을 이용하여 벡터를 특정 길이로 조정할 수 있다. 이를 통해 모델이 학습 데이터의 스케일에 민감하지 않도록 일반화 성능을 강화할 수 있다.\nSimilarity Measurement(유사도 측정)\nNorm은 벡터간의 거리나 유사도를 측정하는데 사용된다. Text관련 task 또는 추천시스템에 자주 활용된다.\nLoss Function(손실함수)\n모델의 예측결과가 실제 데이터로부터 얼마나 벗어나 있는지 측정하기 위해 사용되며, Norm은 그 차이를 정량화하는데 활용된다.\n그럼 이제 가장 대표적인 Norm인 L1 Norm과 L2 Norm에 대해서 알아보자.\n1. L1 Norm # L1 Norm은 Manhattan norm(맨해튼 놈) 또는 Taxicab norm(택시 놈)으로 불려지기도 한다.\nL1 Norm은 벡터의 모든 성분의 절대값을 더한다. 만약 아래와 같은 2차원의 벡터가 있다면,\n이 벡터의 L1Norm은 5가 됩니다.\n위 표기한 것 처럼 L1Norm은\n으로 표기한다.\n여기서 왜 L1 Norm을 택시 놈으로 부르는지 한번 생각해보자.\n원점(0,0)에서 (2,3)까지 택시를 타고 가려면 어떤길로 가더라도 가장 빠르게 갈 수 있는 거리는 5이다.\nL1 Norm은 이런 방식으로 길이를 측정하기 때문에 택시놈이라 불린다.\n즉, 각 점의 위치의 합에 단순히 \u0026lsquo;절대값\u0026rsquo;을 적용하기 때문에, 계단식 거리가 계산되며, 직각 경로 거리가 측정되는 것이다.\nL1 Norm\n2. L2 Norm # L2 Norm은 출발점에서 도착점까지의 거리를 직선거리로 측정한다.\n으로 표현하고, 유클리드 놈(Euclidean norm)이라고 불리기도 한다.\nL2 Norm은 다음과 같이 계산한다.\nL2 Norm\nEuclidean distance가 Manhattan Norm과 다르게, 각 포인트 간의 차이의 절대값의 합산이 아닌, 제곱의 합산에 루트를 적용하는 이유는 아래와 같다.\n1) 수식적으로 Euclidean Distance가 피타고라스의 정의를 따름\n위 피타고라스 정리 공식에서 빗변 C를 구하려면, 제곱합에 루트를 씌워야한다.\n2) 차원의 영향을 줄이기 위한 목적(스케일 조정)\n제곱합은 점들의 차이가 누적된 값이다. 이 값을 루트로 조정하지 않으면, 값의 스케일이 왜곡될 수 있다.\n3) 거리의 기하학적 해석을 보존하기\n루트를 씌우지 않으면 제곱합 값이 나오는데, 이는 거리라기 보다는 거리의 제곱에 해당한다. 따라서, 루트를 씌워야\n직관적으로 이해 가능한 \u0026ldquo;직선거리\u0026quot;를 얻을 수 있다.\n또한, 루트는 값이 작을수록 더 작게, 클 수록 더 크게 표현하기 때문에각 점간의 차이를 좀 더 분명하게 표현할 수 있는 효과를 가진다.\n그렇다면, L1 Norm과 L2 Norm은 머신러닝에서 구체적으로 어떻게 활용될까?\n3. Normalization # Norm은 데이터의 벡터를 특정한 크기로 조정하여, 각 데이터포인트들이 동일한 스케일을 가지도록 조정하는 기능을 할 수 있다.\n먼저, \u0026lsquo;벡터의 Norm\u0026rsquo;은 = \u0026lsquo;벡터의 길이 또는 크기\u0026rsquo;를 의미하는데, 이 성질을 이용하여 벡터의 각 데이터포인트에 Norm 값을 나누어\n모든 데이터포인트를 동일한 스케일안에 둘 수 있게 한다.\n예를들어, 두 특성(feature) A와 B가 있을때, 각 특성에 속하는 값(row, instance)이 A=1, B=1000이라고 하자.\n이때 L2 Norm을 적용하면 이 데이터의 L2 Norm은 아래와 같다.\n그리고 이 값은 대략 1000정도 되는데, 이 Norm값으로 각 특성값을 나누면, 정규화된 벡터는\n**[0.001, 0.9999995]**가 된다.\n따라서, 벡터의 각 요소(1과 1000)를 Norm값인 1000으로 나누면, 모든 특성이 동일한 스케일을 갖게된다.\n물론 위 예시는 \u0026lsquo;행(row)\u0026lsquo;을 기준으로 스케일링을 진행한것으로, 만약 \u0026lsquo;열\u0026rsquo;을 기준으로 스케일링을 진행한다면,\nMin-Max scaling과 Standard Scaling으로 진행하는것이 대표적인 예시이다.\n여기서, 참고로 scaling과 normalization을 혼용해서 설명을 했는데, 물론 유사한 개념이지만,\n엄밀히 따지면 차이점이 존재한다는 점은 알고있어야한다.\nnormalization의 경우 데이터를 특정범위로 조정하는 과정을 의미하며, 데이터의 범위를 [0, 1]또는 [-1, 1]로 조정하는걸 의미한다.\n스케일링은 데이터의 스케일을 조정하는 좀 더 포괄적인, 일반적인 용어로 normalization은 scaling의 한 부분으로 이해할 수 있다.\n4. Loss Function # 두번째로 Norm은 손실함수에서도 중요한 역할을 한다.\n손실함수는 모델의 예측이 실제 데이터로부터 얼마나 벗어나 있는지 측정하기 위해서 사용되고, Norm은 그 차이를 정량화하는데 활용된다.\n그 대표적인 예를 몇가지 살펴보자.\nL2 Norm - Mean Squared Error(MSE) 가장 흔하게 회귀문제에 사용되는 손실함수로, 모델 예측과 실제 값 사이의 유클리드 거리(제곱 거리(차이))의 평균을 계산한다. L2 Norm은 예측 오차의 제곱합으로 큰 오차에 더 큰 패널티를 부여한다. L1 Norm - Mean Absoute Error(MAE) 또다른 회귀 손실함수로, 예측과 실제 값 사이의 \u0026lsquo;절대값\u0026rsquo; 거리의 평균을 사용한다. L1 Norm은 오차의 절대값의 합으로, 이상치에 덜 민감하고 오차의 크기에 따라 선형적인 패널티를 부여한다. 다만, 위에서 L2 Norm의 예시로 MSE를, L1 Norm의 예시로 MAE를 기재하였는데, Norm과 각 loss function이 밀접한 관련이 있지만, 엄밀히 따지면 같은 것은 아니다.\nMSE \u0026lt;-\u0026gt; L2 Norm\nMSE는 예측값과 실제값의 차이를 제곱하여 평균낸것이다. 즉, 각 샘플에 대한 오차를 제곱한 뒤 이를 모두 더하고, 샘플의 수로 나누어 평균을 구한다. 이는 회귀 문제에서 일반적인 손실함수로 많이 활용된다.\nL2 Norm은 벡터의 각 요소의 제곱합의 제곱근을 구하는것으로 벡터의 \u0026lsquo;길이\u0026rsquo;를 측정한다. 이는 MSE와 비슷한 계산과정을 거치지만,\nMSE는 평균을 내는 반면, L2 Norm은 평균을 내지않고! 제곱합의 제곱근을 계산한다.\n결국 MSE는 L2 Norm을 평균화한것으로 볼 수 있다.\nMAE \u0026lt;-\u0026gt; L1 Norm\nMAE는 예측값과 실제값의 차이의 절대값을 평균낸 것이다. 즉, 각 샘플에 대한 오차의 절대값을 모두 더하고, 샘플의 수로 나누어 평균을 구한다.\nL1 Norm은 벡터의 각 요소의 절대값의 합을 구하는 것으로, 이는 벡터 내 요소들의 절대값의 총합을 의미한다.\n결국 MAE는 L1 Norm을 평균한것이라고 볼 수 있다.\n결국, MSE와 L2 Norm, MAE와 L1 Norm은 각각의 계산방식이 유사하며, 오차를 측정하는 방법에서 유래한 개념들이다.\n다만, MSE와 MAE는 머신러닝에서 손실함수로 사용되며 모델의 평균적인 성능을 평가하는 반면, L1 Norm과 L2 Norm은 더\n일반적인 수학적 및 물리적 개념으로 벡터의 길이나 크기를 측정하는데 사용된다.\n# # # # # # Reference # https://bskyvision.com/entry/%EC%84%A0%ED%98%95%EB%8C%80%EC%88%98%ED%95%99-%EB%86%88norm%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80\n[[선형대수학] 놈(norm)이란 무엇인가?\n오늘은 놈(norm)에 대해 설명을 드리고자 합니다. 놈은 노름으로 발음하기도 하는데 둘다 어감이 좀 그렇죠? 선형대수학에서 놈은 벡터의 크기(magnitude) 또는 길이(length)를 측정하는 방법을 의미합\nbskyvision.com](https://bskyvision.com/entry/%EC%84%A0%ED%98%95%EB%8C%80%EC%88%98%ED%95%99-%EB%86%88norm%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80)\n","date":"2024.01.19","externalUrl":null,"permalink":"/posts/80/","section":"글","summary":"","title":"[Norm] L2 Norm? L1 Norm? 이제 제대로 알고 사용하자!","type":"posts"},{"content":"","date":"2024.01.19","externalUrl":null,"permalink":"/tags/l1-norm/","section":"Tags","summary":"","title":"L1 Norm","type":"tags"},{"content":"","date":"2024.01.19","externalUrl":null,"permalink":"/tags/l2-norm/","section":"Tags","summary":"","title":"L2 Norm","type":"tags"},{"content":"","date":"2024.01.19","externalUrl":null,"permalink":"/tags/mae/","section":"Tags","summary":"","title":"Mae","type":"tags"},{"content":"","date":"2024.01.19","externalUrl":null,"permalink":"/tags/mse/","section":"Tags","summary":"","title":"Mse","type":"tags"},{"content":"","date":"2024.01.19","externalUrl":null,"permalink":"/tags/norm/","section":"Tags","summary":"","title":"Norm","type":"tags"},{"content":"","date":"2024.01.19","externalUrl":null,"permalink":"/tags/normalization/","section":"Tags","summary":"","title":"Normalization","type":"tags"},{"content":"","date":"2024.01.19","externalUrl":null,"permalink":"/tags/rmse/","section":"Tags","summary":"","title":"Rmse","type":"tags"},{"content":"","date":"2024.01.19","externalUrl":null,"permalink":"/tags/scaling/","section":"Tags","summary":"","title":"Scaling","type":"tags"},{"content":"real world의 데이터를 분석하고 모델링을 하다보면, 가장 흔하게 발생하는 문제중 하나가 바로 \u0026lsquo;Imbalance Dataset\u0026rsquo;이다.\n머신러닝 모델이 적절하게 학습하기 위해서는, 학습데이터가 예측하고자 하는 목적과 카테고리에 부합한 데이터가\n균형있게 분포해야하고, 또 학습하기에 충분한 양을 가져야한다.\n하지만 real world데이터에서는 불균형한 데이터의 사례가 매우 많다.\n대표적으로 \u0026lsquo;이상거래 탐지(Fraud Detection)\u0026lsquo;를 예로들 수 있다.\n횡령이나, 보이스피싱으로 발생하는 이상거래는 전체 거래중 0.1%도 되지않아, 대표적인 데이터불균형 사례로 꼽힌다.\n이런 과제의 경우, 모델을 개발하고 정확도를 높이기 이전에, 불균형 데이터를 어떻게 처리할 것인지가 관건이 된다.\n오늘은 이러한 불균형 데이터를 학습하기 위한 여러가지 전략에 대해서 정리해보고자 한다.\n불균형데이터를 효과적으로 학습하는 전략은 크게 아래와 같이 분류할 수 있다.\n학습 데이터를 조정하는 방법 다양한 평가기준(metric)을 활용하는 방법 1. 학습데이터를 조정하는 방법 # 가장 대표적인 불균형 데이터 처리 방식으로, 학습 데이터를 직접적으로 조정하여 불균형 현상으로 인한 학습문제를 제거하거나 상쇄하는 전략을 의미한다.\n(1) 미약한 데이터를 더 수집할 수 있는 방안 검토 # 당연한 이야기이지만, 가장 첫번째로, 부족한 target데이터를 더 수집할 수 있는 방안을 검토하고 최대한 데이터 균형을 맞추도록 시도해야한다.\n(2) Resampling # 가장 많이 사용되는 방법으로, 데이터의 리샘플링을 통해 비율을 조정하는 방법이다.\n크게, Over-sampling과 Under-sampling으로 대표된다.\nUnder-Sampling 다수를 차지하는 비율의 데이터를 소수 데이터와 적절하게 분포시키기 위해, 제거하는 방법을 의미한다.\n대표적으로 아래와 같은 under-sampling 기법들이 존재한다.\nRandom under-sampling : 무작위로 데이터를 추출하여 제거하는 방법 Cluster-based Under Sampling : 다수 클래스의 데이터를 클러스터링하여, 각 클러스터에서 대표 샘플(centroid)을 선택함으로써 샘플수를 줄이는 방식. 데이터의 분포를 유지하는데 유리하며, 랜덤샘플링 방식에 비해 정보 손실을 줄일 수 있다. Tomek Links : 다수 클래스와 소수 클래스의 샘플 사이에서 서로 가장 가까운 쌍을 찾고, 다수 클래스의 샘플을 제거함으로써 경계를 더 명확히 나누는 방식이다. 두 샘플이 서로의 최근접 이웃(Nearest Neighbor)이고, 서로 다른 클래스에 속해있다면(경계선을 나누는 기준이 되는 두 샘플) 이들을 Tomek Link라고 한다. 이 방식은 클래스간의 경계를 정제하는데 효과적이고, 결정 결계 주변의 노이즈를 제거하는데 유리한 방법이다. Over-Sampling 오버샘플링은 소수 클래스의 데이터수를 인위적으로 늘려, 다수 클래스의 샘플수와 균형을 맞추는 방법이다.\n가장 간단하게는 Rnadom Over sampling이 있으며,\n대표적으로 SMOTE(Synthetic Minority Over-sampling Technique)방식이 많이 사용된다.\nSMOTE(Synthetic Minority Over-sampling Technique) SMOTE는 소수 클래스의 샘플을 인위적으로 늘려 데이터의 불균형 문제를 해결하는 오버 샘플링 기법 중 하나이다. 이 방법은 소수 클래스의 샘플을 분석하여, 새로운 합성 샘플을 생성함으로써 다수 클래스와 소수 클래스 사이의 균형을 맞추는 방법이다. SMOTE의 기본적인 동작 방식은 다음과 같다.\nSTEP 1. 샘플 선택:\n소수 클래스에서 샘플을 무작위로 선택한다.\nSTEP 2. k-최근접 이웃 찾기:\n선택한 샘플에 대해 k-최근접 이웃(k-Nearest Neighbors) 알고리즘을 사용하여 가장 가까운 소수 클래스 샘플을 찾는다.\nSTEP 3. 샘플 생성:\n선택한 샘플과 그 최근접 이웃 사이를 선형적으로 연결하는 방식으로 새로운 샘플을 생성한다. 이때, 두 샘플 사이를 임의의 비율로 내분하는 점을 새로운 샘플로 추가한다.\nSTEP4. 반복:\n이 과정을 원하는 수의 새로운 샘플이 생성될 때까지 반복한다.\n이 방법을 통해 생성된 데이터는 원래의 데이터 분포를 따르면서도 소수 클래스의 표본 수를 증가시키므로,\n머신러닝 모델이 소수 클래스를 더 잘 학습할 수 있게 된다.\n다만, SMOTE를 사용할 때 주의할 점이 있는데, SMOTE의 방식은 인위적으로 생성된 데이터를 이용하기 때문에 모델이 과적합 (Overfitting)될 위험이 있다.\n따라서 오버샘플링 된 데이터를 사용하여 모델을 학습시킨 후, 반드시 실제 데이터의 분포를 반영하는 테스트셋에서 모델의 평가가\n필요하다.\n2. 다양한 평가 기준을 사용하는 방법 # 불균형 데이터에서 Accuracy metric을 통해 모델을 평가하면, 매우 높은 스코어를 확인할 수 있다.\n당연하다. 100개중 1개만 거짓인 데이터를 학습할 경우 모델은 당연히 높은 확률로 대부분을 \u0026lsquo;참\u0026rsquo;으로 분류할 것이고, 정확도는 99%로\n계산될것이다.\n따라서, Precision, Recall, F1-Score와 같은 metric을 사용하고, ROC-Curves 시각화를 통해 불균형데이터를 학습한 모델이\n소수의 데이터를 제대로 예측해내고 있는지 여러방면으로 검증해야한다.\nConfusion Matrix Confusion Matrix는 실제 클래스와 예측된 클래스의 매칭을 이용하여, 분류 모델을 평가하는 도구이다. 이진 분류 문제에서 실제 클래스는 Positive / Nagative로 나누어져 있고, 분류 모델은 샘플들을 positive / negative로 분류한다. Precision(정밀도) 분류모델이 Positive(정답)으로 예측한것 중 실제로 Positive(정답)인 샘플의 비율 Recall(재현율, 민감도) 실제 Positive(정답)중 분류모델이 Positive(정답)으로 예측한 비율 # 마치며 # 오늘은 Imbalance data를 효과적으로 학습하기 위한 여러가지 전략들을 정리해보았다.\n그동안 실무 데이터를 처리하면서, 단순히 random over sampling 또는 random under sampling을 사용하여\n불균형 데이터를 처리해 왔었는데, 데이터 분포를 고려할 수 있는 여러가지 방법이 있음에도 적용하기 위한 노력이 부족했던것 같다.\n앞으로는 데이터의 분포를 효과적으로 유지할 수 있는 방법인 SMOTE, Cluster-based Under Sampling과 같은 방법을 사용하여\n좀 더 효과적으로 불균형 데이터를 처리할 수 있도록 적용해봐야겠다.\nReference # https://joonable.tistory.com/27\n[resampling으로 imbalanced data(불균형 데이터 문제) 해결하기\nResampling으로 imbalanced data(불균형 데이터 문제) 해결하기 imbalanced data : 데이터 내 각각의 class들이 차지하는 데이터의 비율이 균일하지 않고 한쪽으로 치우친 데이터 major class : dataset 내에서 상대\njoonable.tistory.com](https://joonable.tistory.com/27)\n","date":"2024.01.17","externalUrl":null,"permalink":"/posts/79/","section":"글","summary":"","title":"[Imbalance Data] 불균형데이터를 극복하고 모델을 학습하는 방법","type":"posts"},{"content":"","date":"2024.01.17","externalUrl":null,"permalink":"/tags/imbalance-data/","section":"Tags","summary":"","title":"Imbalance Data","type":"tags"},{"content":"","date":"2024.01.17","externalUrl":null,"permalink":"/tags/over-sampling/","section":"Tags","summary":"","title":"Over Sampling","type":"tags"},{"content":"","date":"2024.01.17","externalUrl":null,"permalink":"/tags/resampling/","section":"Tags","summary":"","title":"Resampling","type":"tags"},{"content":"","date":"2024.01.17","externalUrl":null,"permalink":"/tags/under-sampling/","section":"Tags","summary":"","title":"Under Sampling","type":"tags"},{"content":"","date":"2024.01.17","externalUrl":null,"permalink":"/tags/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B6%88%EA%B7%A0%ED%98%95/","section":"Tags","summary":"","title":"데이터불균형","type":"tags"},{"content":"","date":"2024.01.17","externalUrl":null,"permalink":"/tags/%EB%B6%88%EA%B7%A0%ED%98%95%EB%8D%B0%EC%9D%B4%ED%84%B0/","section":"Tags","summary":"","title":"불균형데이터","type":"tags"},{"content":"딥러닝을 공부하다보면 모든 계산에 행렬곱 공식이 포함되어있는것을 볼 수 있다.\n딥러닝의 neural network은 모두 행렬곱을 기반으로 계산 된다는데, 도대체 어떻게 계산되는건지, 한번 자세하게 알아보자.\n먼저, 행렬곱을 알아보기전에 행렬곱이라는 개념이 포함된 **\u0026lsquo;선형대수학\u0026rsquo;**을 간단히 정리해보면 아래와 같다.\n선형대수학(線型代數學, 영어: linear algebra)은 벡터 공간, 벡터, 선형 변환, 행렬, 연립 선형 방정식 등을 연구하는 대수학의 한 분야이다. 현대 선형대수학은 그중에서도 벡터 공간이 주 연구 대상이다.\n즉, 행렬곱은 선형대수학의 주 연구대상인 벡터공간에서 \u0026lsquo;선형변환\u0026rsquo;(벡터공간의 모든 벡터를 다른벡터로 매핑함)을 표현하는 역할을 한다.\n여기서, 행렬곱이 벡터공간에서 **\u0026lsquo;선형변환\u0026rsquo;**을 수행한다고 했는데, \u0026lsquo;선형변환\u0026rsquo;은 도대체 왜하는건지 궁금할 수 있다.\n선형변환은 머신러닝에서 다양한 사유로 사용되는 개념인데, 예시를 들면 아래 작업들을 위해 활용된다고 이해할 수 있다.\n1) 데이터 차원 축소 고차원 데이터를 낮은 차원으로 변환하여 시각화하거나 분석하기 쉽게 만듦 2) 이미지 처리와 컴퓨터 그래픽스 이미지의 회전, 크기 조절, 기울임과 같은 변형을 구현할 때 선형 변환을 사용한다. 3D 그래픽스에서는 객체를 3차원 공간에서 변환하는 데 필수적\b이다. 3) 데이터 변환과 특성 추출: 기계 학습에서 데이터를 적절한 형태로 변환하거나, 중요한 특성을 추출하는 데 사용된다.\n4) 시스템의 상태 변화 모델링: 물리학, 공학, 경제학 등에서 시스템의 상태를 모델링하고 예측하는 데 사용된다.\n이를 통해 시스템의 다양한 상태와 그 변화를 이해할 수 있게된다.\n덧붙이자면, 기본적으로 행렬곱은 수학적 및 계산적 표현을 간소화하고, 단순한 형태로 변환, 해결하는데 기여하여\n복잡한 문제를 선형적 관점에서 해결하는 데 도움을 준다.\n선형대수학에서 행렬곱은 이러한 방식으로 위의 예시와 같이, 다양한 분야에서 중요한 역할을 하며, 복잡한 문제를 해결하는\n핵심 도구로 활용될 수 있다.\n다시, 선형대수학으로 돌아와서,\n행렬간의 곱을하던, 벡터-행렬 곱을 하던, 선형대수학이 풀고자하는건 \u0026lsquo;연립1차방정식\u0026rsquo;이다. # \u0026lsquo;연립1차방정식\u0026rsquo;이 뭔지 기억나는가?\n아래와 같은 최대차수 항이 \u0026lsquo;1차\u0026rsquo;인 두개의 식이 병합된 식을 \u0026lsquo;연립1차방정식\u0026rsquo;이라고 한다.\n이때, 2개의 식으로 이루어진 연립방정식을 아래와 같이 하나의 식으로 바꿀 수 있다.\n위와 같이, 두개의 식을 \u0026lsquo;행렬\u0026rsquo;과 \u0026lsquo;벡터\u0026rsquo;의 개념을 도입하여 단순화된 식으로 표현할 수 있다. (행렬곱의 기여)\n행렬과 벡터의 곱 # \u0026lsquo;연립1차방정식\u0026rsquo;을 \u0026lsquo;행렬x행백터\u0026rsquo;로, \u0026lsquo;행렬x행백터\u0026rsquo;를 다시 \u0026lsquo;연립1차방정식\u0026rsquo;으로\u0026hellip; # 위와 같이 \u0026lsquo;연립방정식\u0026rsquo;을 하나의 \u0026lsquo;행렬곱\u0026rsquo; 식으로 변환했고, 반대로 \u0026lsquo;행렬곱\u0026rsquo;을 \u0026lsquo;연립방정식\u0026rsquo;으로 다시 변환할 수도 있다.\n위 그림의 행렬곱 식에서 첫번째 행렬과 \u0026lsquo;x,y\u0026rsquo;로 이루어진 행벡터를 곱하면,\n(1 * X) + (2 * y) = 4\n(2 * X) + (5 * y) = 9\n로 정리할 수 있고, 결국 위에서 처음에 정의했던 연립방정식과 동일해진다.\n행렬과 행렬의 곱 # 이번엔 복수의 \u0026lsquo;연립1차방정식\u0026rsquo;을 \u0026lsquo;행렬x행렬\u0026rsquo;으로, 그리고 다시 \u0026lsquo;연립1차방정식\u0026rsquo;으로 연속 변환해보자 # 위 그림을 보면 \u0026lsquo;연립방정식\u0026rsquo;을 \u0026lsquo;행렬 x 벡터\u0026rsquo;로 변환했던 것과 크게 다르지 않다.\n다만, 행렬곱을 수행할때는 인지하고 있어야 할 두가지 제한사항이 있다.\n1) 첫번째행렬의 \u0026lsquo;열\u0026rsquo;의 길이와 두번째행렬의 \u0026lsquo;행\u0026rsquo;의 길이가 같아야 행렬곱 연산이 가능하다.\n즉, 2x3 행렬과 3x2 행렬은 행렬곱이 가능하지만, 3x2 행렬과 3x2 의 행렬은 행렬곱이 불가하다.\n2) 행렬곱을 수행할때, 두 행렬의 위치가 바뀌게 되면 전혀 다른 연산이 되므로 연립방정식을 표현하는 행렬곱은 두개의 행렬의 위치를 바꿀 수 없다.\n그렇다면 위와 같은 행렬곱이 딥러닝에 어떻게 활용되지? # 위와 같은 연립방정식이 있다고 하자.\n이것을 fully-connected layer로 표현해보면,\n1-1) 먼저, input으로 x와 y가 들어오고, 첫번째 식에서 x의 계수가 1이므로, x에 연결된 검정색 엣지의 중요도(weight)가 1로 곱해진다\n1-2) 첫번째 식에서 y의 계수가 2이므로, y에 연결된 검정색 엣지의 중요도(weight)가 2로 곱해지며, 정답은 4가 된다.\n2-1) 두번째식에서 x의 계수가 2이므로, x에 연결된 파랑색 엣지의 중요도(weight)가 2로 곱해진다.\n2-2) 두번째식에서 y의 계수가 5이므로, y에 연결된 파랑색 엣지의 중요도(weight)가 5로 곱해지며, 정답은 9가된다.\n위 Fully-Connected Layer를 행렬곱으로 표현하면 아래와 같다 # 결국, fully-connect layer 층 \u0026lsquo;하나\u0026rsquo;를 통과하는것은 \u0026lsquo;행렬\u0026rsquo;을 \u0026lsquo;하나\u0026rsquo; 곱하는것과 같다.\n추가로, 만약 Layer의 node가 늘어나게 된다면, 행렬의 \u0026lsquo;열\u0026rsquo;이 추가되는것과 같다.\n마치며 # 오늘은 선형대수학에서 행렬곱의 역할과, 행렬곱이 딥러닝에서 어떻게 활용되는지에 대해 살펴봤다.\n가장 궁금했던 부분인 Fully-connceted layer에서 행렬곱이 활용되는 역할에 대해 정리해보자면,\n먼저, 딥러닝에서 layer의 역할은 순전파, 역전파 과정에서 input과 output사이의\n명확한 관계를 찾기 위한 계산을 진행하는 공간의 역할을 한다.\n이때, fully-connected layer에 input되는 x, y를 output(위의 4, 9)과 동일하게 근사하기 위해\n각 엣지, 즉 가중치를 조정해 나가게 된다.\n이때, 각각의 엣지에 할당된 가중치들은 바로 행렬 구조로 이루어져있고(표현할 수 있고),\n우리는 이 행렬값(가중치)을 조정하며, output으로 근사하는 공식(관계)을 찾아 나가게된다.\n즉,\n* 행렬=가중치\n* 행렬곱 = 가중치를 반영하는 과정\n으로 이해할 수 있다.\n이제 다음 단계로는, 위 가중치(행렬의 값)값을 output으로 조정해 나갈때의 기준에 대한 원리 이해가 필요하다.\n다음에는 미분을 통해 위 가중치를 조정하는 방법에 대해서 정리해보도록 하자.\n","date":"2024.01.17","externalUrl":null,"permalink":"/posts/78/","section":"글","summary":"","title":"[행렬곱/행렬과 벡터의 곱]행렬곱이 딥러닝이랑 무슨 관계이길래?","type":"posts"},{"content":"","date":"2024.01.17","externalUrl":null,"permalink":"/tags/%ED%96%89%EB%A0%AC/","section":"Tags","summary":"","title":"행렬","type":"tags"},{"content":"","date":"2024.01.17","externalUrl":null,"permalink":"/tags/%ED%96%89%EB%A0%AC%EA%B3%B1/","section":"Tags","summary":"","title":"행렬곱","type":"tags"},{"content":"ML 프로젝트를 진행할때, 가장 많은 시간이 소요되는 스텝을 꼽아본다면, 아래 과정들을 떠올릴 수 있다.\n데이터 전처리 최적의 성능을 담보할 수 있는 model Architecture를 테스트하고 확정하는 과정 * AutoML이 많은 사용자들에게 관심받기 시작한 것도, 바로 이러한 과정을 자동화해야할 필요가 있었기때문이다.\n\u0026lt;* AutoML이란\u0026gt;\n\u0026hellip;현실의 머신러닝 모델링은 문제 정의 과정에서부터 데이터 수집, 전처리, 모델 학습 및 평가를 거쳐 서비스 적용에 이르기까지에는 여러 분야 전문가들의 많은 시간과 노력이 요구된다. AutoML은 머신러닝을 적용 할 때 마다 이러한 과정을 되풀이하면서 발생하는 비효율적인 작업을 최대한 자동화하여 생산성과 효율을 높이기 위하여 등장하였습니다. 특히, 데이터 전처리 과정에서부터 알고리즘 선택 및 튜닝까지의 과정에서 모델 개발자의 개입을 최소화 하여 품질 좋은 모델을 효과적으로 개발할 수 있는 기술로 AutoML 기술을 머신러닝 모델 개발시에 손쉽게 적용할 수 있는 package toolkit들이 연구 개발되어 오고 있다 \u0026hellip;\nReference\nhttps://medium.com/daria-blog/automl-%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C-1af227af2075\n오늘은 대표적인 AutoML 라이브러리중 가장 최근에 제공된 AutoGluon을 사용하여,\n간결한 코드를 통해, Tabular Dataset에 대한 여러 모델들의 Base performance를 확인하는 튜토리얼을 진행해보자.\nAutoGluon? # AutoGluon은 원시 데이터를 기반으로 머신러닝 모델을 신속하게 생성하고 배포할 수 있도록 설계된 자동화된 머신러닝(automated machine learning, AutoML) 도구이다.\n\bAutoGluon 테이블 데이터, 이미지, 텍스트 및 시계열 데이터에 대한 예측을 지원하며(Python 3.6 이상 지원),\n사용자는 복잡한 기능 엔지니어링이나 데이터 전처리 없이도 빠르게 모델을 생성하고 평가할 수 있고,\n지정된 시간 내에 최적의 모델을 자동으로 선택할 수 있는 기능을 제공한다.\nAutoGluon는 대표적으로 아래 기능들을 제공하고 있다.\n1) 전처리 없이 바로 학습 진행 가능\n2) 최신 모델까지 반영된 종합 모델 학습 API를 \u0026lsquo;Task\u0026rsquo;별로 제공하여 사용자가 간단한 코드만으로 복수의 모델을 학습할 수 있다.\nautogluon.tabular.TabularPredictor : Tabular Dataset에 대한 예측 API autogluon.multimodal.MultiModalPredictor : MultiModal 예측 API autogluon.timeseries.TimeSeriesPredictor : 시계열 데이터셋 예측 API \u0026hellip; 테이블, 텍스트, 이미지 등 다양한 유형에 대한 학습 API 제공 3) 학습이 완료된 후 각 모델별로 train set과 test set에 대한 running time, accuracy등의 정보를 비교확인 할 수 있다.\n4) AWS의 QuickSight를 연동할 경우 분석결과를 시각적으로 표현할 수 있다.\n5) 학습이 중단된 경우에도 다시 학습을 재개할 수 있는 기능 제공\n6) GPU 학습 지원 (* install 참조)\n더보기\n# CPU install pip install -U pip pip install -U setuptools wheel CPU version of pytorch has smaller footprint - see installation instructions in pytorch documentation - https://pytorch.org/get-started/locally/ pip install torch==2.0.1+cpu torchvision==0.15.2+cpu --index-url https://download.pytorch.org/whl/cpu pip install autogluon # GPU install pip install -U pip pip install -U setuptools wheel Install the proper version of PyTorch following https://pytorch.org/get-started/locally/ pip install torch==2.0.1+cu118 torchvision==0.15.2+cu118 --index-url https://download.pytorch.org/whl/cu118 pip install autogluon 좀 더 자세한 내용은 아래 AutoGluon 공식 문서를 통해 확인할 수 있다.\n* https://auto.gluon.ai/stable/install.html\n[Installing AutoGluon\nLinux Pip CPU GPU Conda CPU GPU Source CPU GPU Mac Pip CPU GPU Conda CPU GPU Source CPU GPU Windows Pip CPU GPU Conda CPU GPU Source CPU GPU Install specific AutoGluon modules and dependencies AutoGluon is modularized into sub-modules specialized for tabul\nauto.gluon.ai](https://auto.gluon.ai/stable/install.html)\nTutorial # 1. 설치 # pip install autogluon 2. 데이터 준비 # 테스트를 위해 아래와 같은 간단한 데이터를 준비했다.\n별도로 label encoding, scaling 작업은 진행하지 않았다.\n3. 학습 진행 # # Import import pandas as pd from autogluon.tabular import TabularDataset, TabularPredictor from sklearn.model_selection import train_test_split # Dataset setting whole_df = whole_df.sample(frac=1).reset_index(drop=True) # shuffle train_size_ = int(.6 * whole_df.shape[0]) train_dfs = whole_df.iloc[:train_size_, :] test_dfs = whole_df.iloc[train_size_:, :] train = TabularDataset(train_dfs) test = TabularDataset(test_dfs) # Train predictor = TabularPredictor(label=\u0026#39;label\u0026#39;, eval_metric=\u0026#39;f1_macro\u0026#39;).fit(train, time_limit=3600) 위와 같이 Train, Test dataset을 셋팅하는 부분과 학습코드 단 1줄로 학습 준비가 완료된다.\n해당 Task는 Tabular Dataset이므로, autogluon.tabular.TabularPredictor API를 통해 학습을 진행하였다.\n[Parameter] \u0026rsquo;label\u0026rsquo; 학습할 데이터셋에서 target column을 정의\n\u0026rsquo;eval_metric\u0026rsquo; loss function을 정의하며, 사용자가 직접 정의하지 않을 경우, 적절한 loss func을 내부에서 자동 설정한다.\n\u0026rsquo;time_limit\u0026rsquo; 전체 모델을 학습할때 한계 학습시간을 설정한다. 단일 모델에 대한 Limit가 아니고, 전체 학습시간에 대한 limit이다. 만약 time_limit를 초과할 경우, 현재 학습된 모델에서 학습을 멈추며, 학습된 모델 중 Best model을 선택한다.\n학습데이터가 클 경우, 불과 몇개 모델만 학습될 수 있으며, 이를 방지하기 위해서는 time_limit를 크게 설정하거나, 데이터셋의 크기를 줄이는 방법이 있다. [OUTPUT]\n-\u0026gt; 출력된 결과를 보면, 데이터셋의 정보와 함께 Task가 binary classificatioin 이라는 점도 자동으로 인식한다.\n-\u0026gt; 차례대로 모델들을 학습하고 있으며, 나의 경우 현재 LightGBM에 대해서 importError가 발생해서, 패스한것을 확인할 수 있다.(버전문제인것같다..)\n4. 각 모델의 평가 결과 확인 # 학습이 끝난 후 leaderboard 매서드를 통해 손쉽게 각 모델별 성능과 running time정보를 확인할 수 있다.\n5. AutoGluon 사용 후기 # 오늘은 간단한 classification task를 autogluon을 통해서 진행해보았다.\n다양한 모델에 대해서 각각의 학습코드를 짜고 성능을 확인할 필요가 없고,\n결과를 손쉽게 정리해볼 수 있다는점에서 매우 유용한 라이브러리로 보여진다.\n다만, 워낙 많은 모델들을 학습하다보니, 아무래도 시간이 매우 오래걸리는 문제가 있어, 대용량데이터에 대해서 학습평가를 진행하\u001f것이 쉽지는 않아보인다. (물론 GPU로 진행하면 다를 수 도 있겠다. 이건 추후에 테스트해보도록 하고\u0026hellip;)\n따라서 진행할 프로젝트에 대해 각 모델들의 base performance를 빠르게 확인하고 prototype의 진행여부를 판단할 수 있는 좋은 라이브러리로 앞으로 활용할 기회가 많지 않을까 생각된다.\n","date":"2024.01.16","externalUrl":null,"permalink":"/posts/77/","section":"글","summary":"","title":"[AutoML] AutoGluon으로 빠르게 여러 ML모델들을 비교 테스트해보자!","type":"posts"},{"content":"","date":"2024.01.16","externalUrl":null,"permalink":"/tags/autogluon/","section":"Tags","summary":"","title":"Autogluon","type":"tags"},{"content":"","date":"2024.01.16","externalUrl":null,"permalink":"/tags/automl/","section":"Tags","summary":"","title":"Automl","type":"tags"},{"content":"","date":"2024.01.16","externalUrl":null,"permalink":"/tags/aws/","section":"Tags","summary":"","title":"Aws","type":"tags"},{"content":"","date":"2024.01.16","externalUrl":null,"permalink":"/tags/baseline-model-test/","section":"Tags","summary":"","title":"Baseline Model Test","type":"tags"},{"content":"","date":"2024.01.16","externalUrl":null,"permalink":"/tags/ml/","section":"Tags","summary":"","title":"Ml","type":"tags"},{"content":"","date":"2024.01.16","externalUrl":null,"permalink":"/tags/prototype/","section":"Tags","summary":"","title":"Prototype","type":"tags"},{"content":"보통 PoC단계의 모델개발에서 실제 프로덕트단으로 넘어가는 과정에서 반드시 고려해야할 사항들이 있다.\n바로 모델의 속도와 리소스이다.\n프로덕트단에서 서비스로서 제공되는 모델은 정확도는 기본이고,\n유저의 이벤트에 빠르게 반응해야하며, 대규모의 트래픽에도 운용될 수 있는 적은 리소스로 운용될 수 있어야한다.\n이를 위해서는 모델 개발환경에서부터 모델의 성능, 속도, 리소스에 대한 모니터링과 함께 반복적인 개선작업이 수반되어야한다.\n오늘은 내 모델의 속도와 리소스에 대해 모니터링 할 수 있는 Pytorch Profiler를 활용하기 위해 진행한 튜토리얼 과정에 대해\n정리해보려 한다.\n1. Profiler? # 먼저 프로파일러의 개념에 대해서 잠깐 이해해보도록 하자.\n프로파일러는 \u0026ldquo;머신러닝 모델의 학습과 실행 과정에서 발생하는 다양한 작업과 리소스 사용을 모니터링하고 분석하는 도구를 의미한다.\n특히 대규모 또는 복잡한 머신러닝 모델을 최적화하고 디버깅하는 데 유용하게 사용될 수 있다.\nPytorch에서도 프로파일러를 기본 모듈로 제공하고 있는데, pytorch profiler의 기능에 대해서 조금 더 상세하게 살펴보자.\n1) 성능분석 # 프로파일러는 모델의 학습과 추론 과정에서 각 단계별 실행 시간을 측정한다.\n이를 통해 계산과정에서 성능 병목이 발생하는 구역을 식별할 수 있도록 한다.\n2) 메모리 사용 분석 # 머신러닝 모델, 특히 딥러닝 모델은 종종 대량의 메모리를 사용하게 되는데, 이때, 프로파일러는 메모리 사용량을 추적하여 메모리 누수 또는 비효율적인 메모리 사용을 찾아낼 수 있는 모니터링 기능을 제공한다.\n3) 하드웨어 리소스 사용 분석 # 현재 모델이 사용중인 하드웨어 리소스, CPU, GPU 자원의 사용률과 효율성을 분석할 수 있다.\n수치화 및 시각화를 통해 하드웨어 리소스의 이용도를 최적화할 수 있\b게된다.\n4) 병렬 처리 및 분산 학습 분석 # 대규모 데이터셋이나 복잡한 모델의 경우, 병렬 처리와 분산 학습이 중요하다.\n프로파일러는 이러한 환경에서의 작업 분할과 통신 효율성을 분석할 수 있다.\n🙋‍♂️ 자, 이제 프로파일러의 역할과 기능에 대해서는 대략 정리가 되었으니, 실제 튜토리얼을 진행해보도록 하자.\n먼저, CPU환경에서 모델의 학습 및 추론에 소요되는 시간을 모니터링 해보도록 하자 # import torch import torchvision.models as models from torch.profiler import profile, record_function, ProfilerActivity torch와 profiler를 import한 후, 간단한 resnet모델을 load하여 프로파일링을 진행해보록 하겠다.\nmodel = models.resnet18() inputs = torch.randn(5,3,224,24) with profile(activities=[ProfilerActivity.CPU], record_shapes=True) as prof: with record_function(\u0026#34;model_inference_test\u0026#34;): model(inputs) PyTorch 프로파일러는 컨텍스트 관리자를 통해 활성화되며 활용가능한 다양한 parameter들이 있다.\n가장 유용한 Parameter 몇가지에 대해서 정리해보자.\n1) activities\n\u0026lsquo;activities\u0026rsquo;은 파이토치 프로파일러에서 프로파일링할 활동의 종류를 지정하는데 사용되는 매개변수이다.\n- ProfilerActivity.CPU\n: \bCPU 기반의 연산과 작업을 프로파일링 하겠다! 라는 의미로, CPU에서 수행되는 시간과 리소스 사용을 측정한다. CPU활동에는\ntensor연산, 데이터 로딩, 전처리 등이 포함될 수 있다.\n- ProfilerActivity.CUDA\n: CUDA를 사용하는 GPU기반의 연산을 프로파일링한다는 의미이고, CUDA에서 이뤄지는 작업은 대표적으로 tensor연산, 뉴럴넷의\nfeed-forward 연산과 back-propagation등으로 해당 작업들의 리소스사용량과 속도를 모니터링 할 수 있다.\n2) record_shapes\n텐서 연산에 사용되는 input shape을 기록하여, 각 여난에서 처리되는 데이터 차원의 크기를 모니터링하고 성능문제의 원인을 좀 더 쉽게 분석할 수 있도록 한다.\n3) profile_memory\n메모리 프로파일링을 활성화하는 옵션으로 이를 사용하면 CPU 및 GPU에서 메모리할당과 해제가 기록된다. 메모리 사용량을 추적해서 메모리관련 성능 문제를 식별하는데 유용한 옵션이다.\n4) use_cuda\nCUDA를 사용하는 경우에만 관련이 있는 옵션으로, CUDA 기반의 GPU 작업 시간을 측정하기 위해 사용되며, GPU의 성능 분석에 중요한 정보를 모니터링 할 수 있는 옵션이다.\n자, 다시 코드로 돌아와서 위 파라미터들을 직접 확인해보자!\nwith profile(activities=[ProfilerActivity.CPU], record_shapes=True) as prof: # 위 코드는 프로파일러를 시작하는 컨텍스트 매니저이다. 이 안에서 실행되는 코드에 대해 성능을 기록하게 된다. with record_function(\u0026#34;model_inference_test\u0026#34;): #\u0026#39;model_inference_test\u0026#39;라는 이름으로 모델의 추론 테스트를 기록하며, 아래 코드에서 추론을 시작하게된다. model(inputs) 위 코드는 CPU활동을 기록하기 위한 프로파일러 사용을 시작하는 내용으로 \u0026lsquo;record_shapes=True\u0026rsquo;를 통해 model의 input shape\n또한 기록하는것으로 컨텍스트 매니저를 정의하고 실행하게 된다. (프로파일러는 컨텍스트 매니저(\u0026lsquo;profile\u0026rsquo;)를 통해 실행된다.)\n그리고, 아래 코드를 통해 프로파일러에서 수집된 성능데이터를 요약하여 표 형태로 출력할 수 있다.\n여기서 \u0026lsquo;prof\u0026rsquo;는 앞서 프로파일러 컨텍스트 매니저로 생성된 객체를 의미한다.\nprof.key_averages().table(sort_by=\u0026#34;cpu_time_total\u0026#34;, row_limit=10) # key_averages(): 프로파일링된 데이터의 평균값을 계산한다. 각 종류의 연산이 얼마나 자주 발생했는지, 그리고 평균적으로 얼마나 많은 시간을 차지했는지를 보여준다. # table(...): 계산된 평균값을 테이블 형식으로 출력한다. 이 테이블은 연산의 종류, 평균 실행 시간, 호출 횟수 등 다양한 성능 메트릭을 포함할 수 있다. 출력결과\n예상한대로 대부분의 시간이 컨볼루션에서 소비되는것을 확인할 수 있다.\n위 표에서는 \u0026lsquo;Self CPU\u0026rsquo;와 \u0026lsquo;CPU time\u0026rsquo;이 가장 중요한 정보로 확인해야한다.\n여기서, 보다 세부적인 결과와 함께 input shape을 함께 포함하여 출력하려면 아래와 같이 \u0026lsquo;group_by_input_shape=True\u0026rsquo;를 전달하여 출력하면 된다.\nprint(prof.key_averages(group_by_input_shape=True).table(sort_by=\u0026#39;cpu_time_total\u0026#39;, row_limit=10)) 출력결과\n화면과 같이 우측에 input shapes가 상세히 포함되어 출력되고 있다.\n이번에는 GPU 환경\b에서의 학습 및 추론에 소요되는 시간을 모니터링 해보자. # device = \u0026#39;cuda\u0026#39; if torch.cuda.is_available() else \u0026#39;cpu\u0026#39; with profile(activities=[ ProfilerActivity.CPU, ProfilerActivity.CUDA], record_shapes=True) as prof: with record_function(\u0026#39;model_inference_GPU_TEST\u0026#39;): model(inputs) print(prof.key_averages().table(sort_by=\u0026#39;cuda_time_total\u0026#39;, row_limit=10)) 위와 같이 컨텍스트 매니저(profile로 선언된 부분)에서 \u0026lsquo;ProfilerActivity.CUDA\u0026rsquo;를 통해 GPU 리소스에 대한 모니터링을 진행할 수 있도록 하고, 마찬가지로 input shape을 출력하게끔 정의한 후 \u0026lsquo;model_inference_GPU_TEST\u0026rsquo;라는 이름으로 모델의 추론 테스트를 기록하도록 했다.\n출력결과\n출력결과의 오른쪽에서 CUDA기반의 GPU에 대한 소요시간을 확인할 수 있다.\nPytorch Profiler는 소요시간뿐 아니라, CPU와 GPU환경에서의 메모리 소비에 대한 모니터링도 제공한다. # 아래와 같이, 컨텍스트 매니저에서 간단하게 \u0026lsquo;profile_memory=True\u0026rsquo;옵션을 통해 메모리 사용량에 대한 정보를 출력할 수 있다.\ninputs = torch.randn(1,32) model = MLPmodel(inputs.size(1), inputs.size(1)) with profile(activities=[ProfilerActivity.CPU], profile_memory=True, record_shapes=True) as prof: model(inputs) print(prof.key_averages().table(sort_by=\u0026#39;self_cpu_memory_usage\u0026#39;, row_limit=10)) 출력결과\n위와 같이 프로파일러는 하드웨어 리소스에 대한 상세한 정보를 제공하지만, 매번 일시적인 출력으로만 모니터링을 진행할 수 없다.\n당연하게도 Pytorch 프로파일러는 해당 내용을 json 형태의 파일로 적재 또한 가능하다.\nmodel = models.resnet18().to(device) inputs = torch.randn(5, 3, 224, 224).to(device) with profile(activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA]) as prof: model(inputs) prof.export_chrome_trace(\u0026#39;/home/jovyan/WIDE_HOME/wontae_kim/Research/trace.json\u0026#39;) 위 export_chrome_trace를 통해 모니터링 데이터를 적재할 수 있으며,\n\u0026ldquo;chrome://tracing/\u0026rdquo; 을 통해 log화 된 데이터를 시각화로 확인할 수 있다.\n마치며 # 오늘은 Pytorch에서 제공되는 Profiler 라이브러리를 사용해 간단한 ResNet모델의 추론과정을 모니터링하는 튜토리얼을 진행해보았다. 간단한 프로파일링만으로 상당히 유용한 정보를 많이 제공하고있는데, 사실 파이토치에서는 TensorBoard\b에서 프로파일러를 통해 기록한 내용을 좀 더 상세하게 모니터링 할 수 있다.\n다음에는 TensorBoard를 통해 모델을 모니터링 하고 분석하여 인사이트를 얻는과정에 대해서 좀 더 상세하게 리뷰할 수 있도록 해보자.\nReference # https://pytorch.org/tutorials/recipes/recipes/profiler_recipe.html\n[PyTorch Profiler — PyTorch Tutorials 2.2.0+cu121 documentation\nNote Click here to download the full example code PyTorch Profiler This recipe explains how to use PyTorch profiler and measure the time and memory consumption of the model’s operators. Introduction PyTorch includes a simple profiler API that is useful w\npytorch.org](https://pytorch.org/tutorials/recipes/recipes/profiler_recipe.html)\n","date":"2024.01.10","externalUrl":null,"permalink":"/posts/76/","section":"글","summary":"","title":"[Pytorch Profiler]파이토치 프로파일러를 통해 모델 성능 모니터링하기!","type":"posts"},{"content":"","date":"2024.01.10","externalUrl":null,"permalink":"/tags/profiler/","section":"Tags","summary":"","title":"Profiler","type":"tags"},{"content":"","date":"2024.01.10","externalUrl":null,"permalink":"/tags/%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81/","section":"Tags","summary":"","title":"모니터링","type":"tags"},{"content":"","date":"2024.01.10","externalUrl":null,"permalink":"/tags/%ED%8C%8C%EC%9D%B4%ED%86%A0%EC%B9%98/","section":"Tags","summary":"","title":"파이토치","type":"tags"},{"content":"","date":"2024.01.10","externalUrl":null,"permalink":"/tags/%ED%94%84%EB%A1%9C%ED%8C%8C%EC%9D%BC%EB%9F%AC/","section":"Tags","summary":"","title":"프로파일러","type":"tags"},{"content":"오늘은 \u0026lsquo;확률 변수\u0026rsquo; 와 \u0026lsquo;**확률 분포\u0026rsquo;**에 대해서 정리해보도록 하자.\n확률에 대해서 이해하기 위해서는 먼저 시행(trial)과 사건(event)를 이해해야한다.\n예를들어,\n주사위를 던져서 특정 눈금값을 구한다\n라는 행위를 할때, 주사위를 던지는 행위는 \u0026lsquo;시행(trial)\u0026lsquo;이라고 할 수 있고, 그 결과로 나온 눈금값이 나온 결과를 \u0026lsquo;사건(event)\u0026lsquo;라고 정의할 수 있다.\n즉, \u0026lsquo;확률\u0026rsquo;이라는 것은 \u0026lsquo;어떤 사건이 일어날 가능성\u0026rsquo;을 수치로 표현한 것이다.\n첫번째 알아볼 개념 : 확률변수(Random Variable)와 확률 함수 # 확률변수란 \u0026lsquo;시행(trial)할 때마다 변할 수 있는 값\u0026rsquo;을 의미한다.\n그리고 \u0026lsquo;확률 함수\u0026rsquo;란 각 확률변수에 따라서 \u0026lsquo;확률 값\u0026rsquo;을 부여하는 함수를 의미한다.\n이때, 확률함수는 일반적으로 \u0026lsquo;P\u0026rsquo;라고 표현한다.\n예시를 수식으로 표현해보자.\n주사위를 던질(시행)때, 눈금이 3이 나올(사건) 확률은 1/6(확률값) 이다.\n이를 수식으로 표현하면\n\u0026ldquo;P(X=3) = 1/6\u0026rdquo;\n으로 표현할 수 있다.\n두번째 알아볼 개념 : 확률분포(Probability Distribution) # 먼저, 발생가능한 모든 사건들의 집합(전체집합)을 \u0026lsquo;표본 공간(sample space)\u0026lsquo;라고 한다.\n이 표본공간에서 시행에 의해서 사건이 일어날 경우, 그 사건에 해당하는 변수가 \u0026lsquo;확률변수\u0026rsquo;인것이다.\n여기서 확률 분포(probability distribution)란, 각 사건에 어느정도의 확률이 할당되었는지 표현한 정보를\n의미한다.\n즉, 주사위를 던졌을때, 1이 일어날 확률, 2가 일어날 확률, 3이 일어날 확률 \u0026hellip;6이 나올 확률에 대한\n각각의 값들을 표현한 정보를 의미한다.\n보통의 데이터에서 확률 분포를 시각화하면 아마도, 아래와 같이 표현될 것이다.\n그리고, 확률변수 X에, 변수 \u0026lsquo;X\u0026rsquo;가 발생할 확률, 즉 X에 대한 확률분포를 대응시키는 함수를 **\u0026lsquo;확률분포함수\u0026rsquo;**라고 한다.\n즉, 확률분포함수를 위 확률분포라는 개념과 함께 정의하면,\n모든 사건에 대해 확률 분포 함수를 적용하고 그 결과값 들을 모아서 표\u001d현한 것을 \u0026lsquo;확률 분포\u0026rsquo;라고 이해할 수 있다.\n[참고]\n확률 분포 그 자체를 함수로 보고, 확률분포함수 \u0026lsquo;P\u0026rsquo;와 같은 의미로 쓰기도 한다.\n자 이제, 확률분포에 대해서 조금 더 깊게 들어가보자.\n먼저, 확률분포는 다시 이산확률분포와 연속확률분포 두가지의 확률분포로 나눠서 정의할 수 있다.\n세번째 알아볼 개념 : 이산확률분포와 연속확률분포 # 먼저, 이산확률분포란, 확률변수 X의 개수(나올 수 있는 변수)가 셀 수 있을 때, 이를 **\u0026lsquo;이산확률변수\u0026rsquo;**라하고, 그 분포는 \u0026lsquo;이산확률분포\u0026rsquo; 라고한다. 반대로 확률변수를 셀 수 없다면 **\u0026lsquo;연속확률변수\u0026rsquo;**라하고, 그 분포를 \u0026lsquo;연속확률분포\u0026rsquo; 라고 한다.\n그리고 위 확률분포를 계산하는 가장 대표적인 학률 분포 함수로 아래 2가지 함수가 있다.\n확률질량함수\n확률밀도함수\n각각 이산확률변수와 연속확률변수, 어느쪽의 확률을 계산하는가에 따라 위 함수를 각각 구분하여\n사용하게 된다.\n먼저, 확률질량함수는 이산확률변수가 특정한 값을 가질 확률을 출력하는 함수이다.\n즉, 확률질량함수는 이산확률분포를 표현하기위해 사용하는 확률분포함수로 이해할 수 있다.\n예를들어, 동전 2개를 동시에 던지는 시행에서 두 눈금의 합을 X라고하자.\n이때, X는 이산확률변수로, 확률질량함수 f(x)는 다음과 같이 정의할 수 있다.\nf(0) = P(X=0) = 1/4 f(1) = P(X=1) = 1/2 f(2) = P(X=2) = 1/4 이를 확률변수 X에 대한 \u0026lsquo;확률질량함수\u0026rsquo;라는 의미로 fx(X)라고 표기하기도 한다.\n딥러닝에서 사용되는 확률질량함수의 예시를 몇가지 살펴보자.\n가장 대표적인 예시로 classification model을 들 수 있는데, 분류모델의 출력은 확률분포에 해당한다.\n이때, 위 classification model을 **\u0026lsquo;확률질량함수\u0026rsquo;**로 이해할 수 있으며, 확률분포는 아래와 같이 나타낼 수 있다.\n두번째, 연속확률변수의 예시로는 \u0026lsquo;키, 달리기성적, 몸무게 \u0026hellip;\u0026lsquo;와 같은 변수들을 예로 들 수 있다.\n예를들어, 사람의 키는 실수 값을 가지기 때문에, 값의 개수를 셀 수 없다.\n따라서, 키가 정확히 173.32945\u0026hellip;일 확률일 얼마일까? 라는 문제를 풀고자할 때,\n키가 170이상 175 미만일 확률을 구하는 방식으로 결과를 구할 수 있다. 이런 방식을 확률밀도함수라고 한다.\n즉, 확률밀도함수란 연속확률변수가 주어진 구간내에 포함될 확률을 출력하는 함수를 의미한다.\n확률밀도함수의 예시\n마치며 # 오늘은 확률분포와 확률변수에 대해서 기본적인 개념들을 정리해보았다.\n머신러닝 모델로써 문제를 해결하고자할 때, 목표값이 될 확률변수라는 개념과 머신러닝 모델이 결과를 도출하는 방식인 확률분포에 대해서\n개념적으로 정리할 수 있었다.\n다음에는 이산확률분포와 연속확률분포에 대해서 조금 더 깊게 살펴보고 이해해보도록 해보도록 하자.\n","date":"2023.12.25","externalUrl":null,"permalink":"/posts/73/","section":"글","summary":"","title":"[ML 확률과 통계] 확률 변수와 확률 분포","type":"posts"},{"content":"","date":"2023.12.25","externalUrl":null,"permalink":"/tags/%EC%97%B0%EC%86%8D%ED%99%95%EB%A5%A0%EB%B6%84%ED%8F%AC/","section":"Tags","summary":"","title":"연속확률분포","type":"tags"},{"content":"","date":"2023.12.25","externalUrl":null,"permalink":"/tags/%EC%9D%B4%EC%82%B0%ED%99%95%EB%A5%A0%EB%B6%84%ED%8F%AC/","section":"Tags","summary":"","title":"이산확률분포","type":"tags"},{"content":"","date":"2023.12.25","externalUrl":null,"permalink":"/tags/%ED%99%95%EB%A5%A0%EB%B0%80%EB%8F%84%ED%95%A8%EC%88%98/","section":"Tags","summary":"","title":"확률밀도함수","type":"tags"},{"content":"","date":"2023.12.25","externalUrl":null,"permalink":"/tags/%ED%99%95%EB%A5%A0%EB%B3%80%EC%88%98/","section":"Tags","summary":"","title":"확률변수","type":"tags"},{"content":"","date":"2023.12.25","externalUrl":null,"permalink":"/tags/%ED%99%95%EB%A5%A0%EB%B6%84%ED%8F%AC/","section":"Tags","summary":"","title":"확률분포","type":"tags"},{"content":"오늘부터, 통계의 중요한 요점들에 대해서 공부하고 정리해보려한다.\n가장 기본적인 통계 개념부터 부터 가볍게 지나치지않고 상세하게 정리해가보도록 하자.\n먼저, 확률이란? # 확률이란, 특정한 사건이 일어날 가능성을 수로 표현한것을 의미한다.\n그리고, 확률은 0부터 1사이의 실수로 표현되는데, 어떤 사건에 대해 수치표현을 통해\n객관적이고 합리적인 의사결정을 할 수 있는 근거가 된다.\n기계학습 모델을 확률적으로 이해해보자 # 우리는 N개의 학습 데이터로 기계학습 모델을 학습한다.\n이때, 일반적으로 기계학습 모델의 출력은\u0026rsquo;확률\u0026rsquo;형태를 띤다.\nex) image classification model\n\u0026lsquo;θ\u0026rsquo;가 이미지 x에 대해서 0.75의 확률로 \u0026lsquo;고양이\u0026rsquo;일것이라고 예측했다.ex) text generation model \u0026lsquo;θ\u0026rsquo;가 \u0026lsquo;나는 밥을\u0026rsquo; 이후에 \u0026lsquo;먹었다\u0026rsquo;라는 단어가 나올 확률을 0.42로 예측했다.\n경우의 수 # 그리고, 확률을 이해하기 위해 \u0026lsquo;경우의 수\u0026rsquo;라는 개념을 먼저 이해해야한다.\n그리고, 경우의 수를 계산하기 위한 방법으로는 **순열(Permutation)**과 **조합(combination)**이 있다.\n순열과 조합을 하나씩 알아보자.\n순열(Permutation)\n서로 다른 n개에서 r개를 중복 없이 뽑아 특정한 순서로 나열하는 것을 의미한다.\n다시 풀어쓰면, **\u0026ldquo;서로다른 n개에서 r개를 뽑아 순서가 있게 나열하는 것\u0026rdquo;**이라고 정리할 수 있다.\n예를들어, a,b,c 세개의 알파벳중 2개를 뽑아 순서있게 나열하는 경우는 아래와 같다.\nab\nac\nba\nbc\nca\ncb\n이처럼 나열할때, \u0026lsquo;나열하는 개수\u0026rsquo;를 \u0026lsquo;순열의 수\u0026rsquo;라고 한다.\n즉, a,b,c 3개의 단어중 2개를 뽑는 순열의 수는 위의 6개라고 할 수 있다.\n순열을 표현하는 방법이 여러가지라 헷갈릴 수 있는데, 아래와 같이 이해해보자.\n용어 의미 예시 순열 순서가 있는 나열 n개에서 r개를 택하는 순열 n개에서 r개를 뽑아 순서있게 나열 a,b,c에서 2개를 뽑아 나열 순열의 수 순서가 있게 나열하는 경우의 수 a,b,c에서 2개를 뽑아 나열하는 경우의 수 : 6가지 우리는 지금 \u0026lsquo;순열\u0026rsquo;에 해당하는 케이스에서 경우의 수를 구해야하기때문에,\n\u0026lsquo;순열의 수\u0026rsquo;를 좀 더 간편하게 구하는 방법을 확인해보자.\n위 a,b,c에 대해 2개를 택하는 순열을 나열하는 경우를 다시 대입해보면,\n\u0026lsquo;a\u0026rsquo;뒤에 올 단어는 \u0026lsquo;b\u0026rsquo;또는 \u0026lsquo;c\u0026rsquo;이며, \u0026lsquo;a\u0026rsquo;는 당연히 중복해서 나올 수 없다.\n마찬가지로, \u0026lsquo;b\u0026rsquo;뒤에 나올 단어는 \u0026lsquo;a\u0026rsquo;또한 \u0026lsquo;c\u0026rsquo;이며, \u0026lsquo;b\u0026rsquo;는 중복해서 나올 수 없다.\n수식으로 바꿔보면,\n첫번째 자리에 올 수 있는 경우의 수는 : n개\n두번째 자리에 올 수 있는 경우의 수는 : n-1개\n세번째 자리에 올 수 있는 경우의 수는 : n-2개\n\u0026hellip;\nr번째 나올 수 있는 경우의 수는 : n-(r-1)개\n즉, 이걸 이어붙이면, n x (n-1) x (n-2) x \u0026hellip; x n-(r-1) 의 공식이 완성된다.\n순열은 \u0026lsquo;Permutation\u0026lsquo;이라고하는데, 앞 글자를 따서,\nn개에서 r개를 뽑는 순열의 수를 구한다를 \u0026rsquo;nPr\u0026rsquo;이라고 표현할 수 있고,\n순열의 수를 구하는 공식은 아래와 같이 요약 정의할 수 있다.\n* nPr = n x (n-1) x (n-2) x \u0026hellip; x n-(r-1)\n여기서 끝이 아니다. 위 공식을 \u0026lsquo;팩토리얼\u0026rsquo;이라는 개념으로 좀 더 간략하게 표현해보자.\n(팩토리얼로 표현할 경우, 좀 더 식이 간결해지고, 계산이 편리해지기 때문이다.)\n* nPr = n x (n-1) x (n-2) x \u0026hellip; x n-(r-1)\n다시, 위 식의 우변은 몇개의 인수가 곱해져있을까?\n바로 r개이다. n부터 1씩 줄어드는 배열이 r개가 있고, 이 r개의 인수가 곱해진것이다.\n식을 간소화 하기 위해, 위 식의 우변 **\u0026lsquo;분자\u0026rsquo;**와 **\u0026lsquo;분모\u0026rsquo;**에 (n-r)!을 곱해보자.\n분자는 n!과 같습니다. 따라서 아래와 같이 변형될 수 있다.\n1) 순열의 수 공식(n = r 일때)\nnPr = n! (n팩토리얼)\n2) 순열의 수 공식(n!=r 일때)\n참고)0!은 1이다.\n그렇다면, 머신러닝 모델을 학습하는 과정에서 \u0026lsquo;순열\u0026rsquo;이라는 개념은 언제 활용될까?\n위 그림과 같이 각 Epoch으로 model에 데이터가 input될때, 데이터는\n매번 일정한 순서로 투입되지 않는다.\n그리고, 데이터가 투입되는 경우의 수는 순열의 개념이 도입되어,\nnPn이 된다.\npython을 이용하면 간단하게 순열을 계산할 수 있다.\n조합(Combination)\n서로 다른 n개에서 r개를 중복없이 순서를 고려하지 않고 뽑는것을 의미한다.\n\u0026rsquo;n개에서 r개를 뽑는 경우의 수\u0026rsquo;라는 개념은 순열과 동일하나, 순서를 고려하지 않고 뽑는다는 점에서 순열과 다르다.\n아래의 그림을 통해 그 개념은 간단하게 이해할 수 있다.\n마찬가지로, combination또한 python을 통해 간단하게 구현할 수 있다.\n# 중복순열 (Permutation with Repetition)\n서로 다른 n개에서 중복을 포함해 r개를 뽑아 특정한 순서로 나열하는 것을 의미한다.\n모든 중복 순열의 수(경우의 수)는 다음의 공식으로 계산할 수 있다.\n\u0026lt;중복 순열의 수 공식\u0026gt;\n예시)\nQ. 세 개의 숫자 1,2,3을 이용해 만들 수 있는 두자리의 자연수의 개수는?\n(단 같은 숫자를 여러 번 사용하거나, 사용하지 않은 순자가 있어도 괜찮다. 본인을 포함하는 조합을 경우에 포함시키는 방법)\n모든 경우 : 11, 12, 13, 21, 22, 23, 31, 32, 33 A. 경우의 수 : 3\b² = 9\npython 으로 계산해보자\n[Output]\n# 중복조합 (Combination with Repetition)\n서로 다른 n개에서 중복을 포함해 순서를 고려하지 않고, r개를 뽑는것을 의미한다. 모든 중복 조합의 수(경우의 수)는 다음의 공식으로 계산할 수 있다. 중복 조합의 수 공식 예시) 딥러닝에서는 학습된 여러 모델의 결과를 활용하여 최종적인 결과를 생성하는 **앙상블(Ensemble)**방법이 존재한다.\nensemble의 케이스에 대해서 조금 더 자세히 살펴보자.\n[전제]\n서로 다른 딥러닝 archetecture 1번, 2번, 3번이 존재한다. 중복을 허용하여 4개를 골라서 딥러닝 모델 4개를 학습하고자 한다. [문제]\n이때, 가능한 학습의 모든 조합의 수는?\n단, 같은 아키텍처를 여러번 사용하거나, 선택하지 않는 아키텍처가 있어도 된다.\n이말인즉슨, 학습할 4개의 모델을 [1,2,3,1]로 선택할 수도 있고, 1번 archetecture를 학습하여 [1,1,1,1]도 가능하다.\n학습 순서는 상관없기 때문에, [1,2,3,1]과 [1,2,1,3]은 같은 경우에 속한다.\n위 케이스는 python을 통해서 간단하게 구할 수 있다.\n[output]\n# # Reference # 1) Blog\nhttps://hsm-edu.tistory.com/1484\n","date":"2023.12.21","externalUrl":null,"permalink":"/posts/72/","section":"글","summary":"","title":"[ML 확률과 통계] 순열과 조합","type":"posts"},{"content":"","date":"2023.12.21","externalUrl":null,"permalink":"/tags/combination/","section":"Tags","summary":"","title":"Combination","type":"tags"},{"content":"","date":"2023.12.21","externalUrl":null,"permalink":"/tags/permutation/","section":"Tags","summary":"","title":"Permutation","type":"tags"},{"content":"","date":"2023.12.21","externalUrl":null,"permalink":"/tags/%EA%B2%BD%EC%9A%B0%EC%9D%98-%EC%88%98/","section":"Tags","summary":"","title":"경우의 수","type":"tags"},{"content":"","date":"2023.12.21","externalUrl":null,"permalink":"/tags/%EC%88%9C%EC%97%B4/","section":"Tags","summary":"","title":"순열","type":"tags"},{"content":"","date":"2023.12.21","externalUrl":null,"permalink":"/tags/%EC%A1%B0%ED%95%A9/","section":"Tags","summary":"","title":"조합","type":"tags"},{"content":"","date":"2023.12.21","externalUrl":null,"permalink":"/tags/%ED%99%95%EB%A5%A0/","section":"Tags","summary":"","title":"확률","type":"tags"},{"content":"","date":"2023.12.19","externalUrl":null,"permalink":"/tags/adwin/","section":"Tags","summary":"","title":"Adwin","type":"tags"},{"content":"","date":"2023.12.19","externalUrl":null,"permalink":"/tags/concept-drift/","section":"Tags","summary":"","title":"Concept Drift","type":"tags"},{"content":"","date":"2023.12.19","externalUrl":null,"permalink":"/tags/cusum/","section":"Tags","summary":"","title":"Cusum","type":"tags"},{"content":"","date":"2023.12.19","externalUrl":null,"permalink":"/tags/ddm/","section":"Tags","summary":"","title":"Ddm","type":"tags"},{"content":"오늘은 concept drift를 detectioin하는 여러가지 방법론들을 살펴보려한다.\n**\u0026lsquo;concept drift\u0026rsquo;**는 시간이 지남에 따라 모델에 input되는 데이터의 통계적 특성이 달라지거나,\nfeature와 label간의 관계가 달라질때 model의 성능이 저하되는 경우를 일컫는다.\nconcept drift\b가 data drift에 비해 갖는 특징중 하나는, 모델의 도메인에 따라 중요성이 달라질 수 있다는 것이다.\n가령, 얼굴인식 또는 STT, TTS, 언어모델과 같은 경우에는 데이터의 특성이 달라지지 않기 때문에 concept drift가 발생할 확률이 낮다.\n하지만, 그 외 과제를 해결하기 위한 예측모델의 경우 데이터 분포나 feature와 label의 관계에 변화가 생기는 concept drift 문제가\n빈번하게 발생할 수 있기때문에 중요하게 고려되어야한다.\n그리고, concept drift는 모델이 개발 및 배포된 후 프로덕트단에서 발생하는현상이지만,\n모델 개발 및 데이터 전처리 단계에서도 이를 최소화 하기 위한 여러 조치를 취할 수 있다.\n따라서, concept drift를 예방하고 detection할 수 있는 방법들을 차례로 알아보도록 하자.\n1. 모델 개발 단계에서 Concept Drift를 피하기 위해 고려해야하는 점 # 1) 학습과정에서 최대한 다양한 데이터를 사용한다. # 프로덕트 단에서는 다양한 형태의 예측하지 못한 데이터가 input될 수 있기 때문에, 수집할 수 있는 최대한 다양한 데이터를 활용해\n모델을 학습해, 프로덕트 단에서도 concept drift에 강건한 모델이 될 수 있도록 할 수 있다.\n예를들어, 온라인에서 많이 팔릴 제품에 대한 예측모델을 개발한다고 가정할때, 시즌별로 구매패턴이 다를것이므로,\n계절 또는 연말, 연초등 다양한 시기의 데이터를 통해 학습하면, 시즌에 영향을 덜 받는 모델을 만들 수 있다.\n2) Feature Engineering # 이와는 반대로 모델 학습 시 위 1)번 항목에서는 프로덕트에서 발생할 다양한 데이터를 통해 학습하는 방법을 설명했다면,\n모델학습 전 Feature를 선택할때, 일관되고 시계열에 영향을 많이 받지않는 일정한 특성을 지닌 Feature를\n선택할 수도 있다.\n3) 모델 선택 # 모델을 선택할때, 단일 모델이 아닌 여러 특성을 지닌 데이터에 대응할 수 있는 앙상블 형태로 모델을 구축할 경우,\nConcept Drift에 좀 더 강건한 모델을 만들 수 있다.\n4) 정기적인 모델 업데이트 및 재학습 # 1번에서는 프로덕트단에서 발생할 수있는 다양한 데이터를 미리 학습 당시에 반영하여 최대한 다양한 데이터패턴을 학습한다면,\n반대로 짧은 주기를 가지고 적시의 데이터로 모델을 업데이트하고 재학습하여 concept drift를 피하고\n안정적인 performance를 유지할 수 있다.\n# 2. Drift Detection Algorithms # 다음은 실시간 데이터 스트림에서 Drift를 감지하는 데 사용되는 알고리즘들이다.\n이 알고리즘들은 데이터 스트림의 통계적 특성이 시간이 흐름에 따라 변화하는지 여부를 식별하는데 중점을 둔다.\n1) ADWIN (Adaptive Windowing) # ADWIN은 데이터스트림에서 변화를 감지하기 위해 동적으로 조정되는 window의 크기를 기반으로,\n두개의 연속적인 데이터 블록간의 평균값이 통계적으로 유의미하게 차이나는지 확인하고, 데이터 스트림 내 변화가 발생하는 시점을 감지하는 방법이다.\n아래 예시를 보자.\n먼저, 가장 처음에는 모든 데이터 포인트를 포함하는 하나의 window(전체 데이터스트림이라보면 됨)로 시작한다.\nex)\n주어진 데이터 스트림 : [1, 1, 1, 1, 1, 1, 0.8, 0.8, 0.6, 0.6, 0.4, 0.4, 0.2, 0.2, 0]\n초기 window : [1, 1, 1, 1, 1, 1, 0.8, 0.8, 0.6, 0.6, 0.4, 0.4, 0.2, 0.2, 0] -\u0026gt; 전체 스트림과 동일\n[1\u0026hellip;n] \u0026lt;- 쪼개어진 window간 평균값을 비교한다-\u0026gt; [n+1 \u0026hellip;m] 평균값이 처음 설정한 임계치(drift를 탐지하기 위한 기준)를 초과할 경우,\n해당 window 경계점의 포인터값이 drift로 의심할 만큼 차이가 커진것으로 간주하고 window의 크기를 조정한다.\nfrom river import drift adwin = drift.ADWIN() data_stream = [0, 1, 0, 0, 1, 1, 0, 1, 1, 1] # 예시 데이터 스트림 for i, value in enumerate(data_stream): adwin.update(value) # ADWIN에 데이터 포인트 업데이트 if adwin.change_detected: print(f\u0026#34;Drift detected index {i}\u0026#34;) 2. Page-Hinkley Test # Page-Hinkley Test는 데이터 스트림의 평균 변화를 모니터링하여 설정된 임계값을 초과하면\n변화가 감지된것으로 판단하여 drift를 감지한다.\n간단한 방법으로 예를들어, [0,0,0,1,1,1,0,0,0]과 같은 데이터스트림이 주어지고,\n임계값을 0.5라고 가정했을때, 첫번째 데이터포인트부터 차례대로 누적평균을 구한다.\n4번째 데이터포인트에서 1-0(이전 데이터포인트)=1이 되어 임계값을 초과하는 변화량을 감지하여\ndrift를 관측하는 방법이다.\nfrom river import drift ph = drift.PageHinkley() data_stream = [0, 1, 0, 0, 1, 1, 0, 1, 1, 1] # 예시 데이터 스트림 for i, value in enumerate(data_stream): ph.update(value) # Page-Hinkley에 데이터 포인트 업데이트 if ph.change_detected: print(f\u0026#34;Drift detected index {i}\u0026#34;) 3. CUSUM (Cumulative Sum Control Chart) # cusum은 데이터스트림에서 작은 변화를 감지하는데 사용되는데, 누적된 차이수가 특정 임계값을 초과할 경우\nDrift가 감지된것으로 판단하는 방법이다.\n예를들어, 주어진 데이터 스트림이 [1, 1, 2 ,2 ,2, 3, 3, 3, 3]이고 임계값이 2일때,\n두번째에서 세번째 인덱스에서 차이가 \u0026lsquo;1\u0026rsquo; 관측되고(누적), 5번째에서 6번째로 넘어갈때 차이가 다시 \u0026lsquo;1\u0026rsquo;관측되어\n누적 차이가 2가되어 임계값에 충족하게된다. 이때, 임계값 이상의 차이가 발생한 이후 시점의\n3,3,3,3에 해당하는 부분에서 Drift가 발생한것으로 간주하는 방식이다.\nimport numpy as np def cusum(data, threshold=5, drift=0): S = 0 change_points = [] for i in range(1, len(data)): S = max(0, S + data[i] - data[i-1] - drift) if S \u0026gt; threshold: change_points.append(i) S = 0 return change_points data_stream = np.random.normal(0, 1, 100) # 예시 데이터 스트림 change_points = cusum(data_stream) print(\u0026#34;Drift points:\u0026#34;, change_points)​ 4. DDM (Drift Detection Method) # DDM은 분류모델에서 오류율의 변화를 감지하는 데 사용되며, 오류율이 특정 수준이상 증가하면 변화가\n감지된것으로 판단하는 방법이다.\n예를들어, classification task에서\n\u0026lsquo;[정답, 정답, 오류, 정답, 오류, 오류, 정답, 정답, 오류, 오류]\u0026rsquo; 과 같이 측정되었다고 가정하자.\n이를 DDM 알고리즘을 통해 오류율의 변화를 모니터링 하고, 임계값은 0.3으로 설정하고 진행해보자.\n첫 번째 데이터 포인트 (정답): 오류율 = 0/1 = 0 두 번째 데이터 포인트 (정답): 오류율 = 0/2 = 0 세 번째 데이터 포인트 (오류): 오류율 = 1/3 ≈ 0.333 네 번째 데이터 포인트 (정답): 오류율 = 1/4 = 0.25 다섯 번째 데이터 포인트 (오류): 오류율 = 2/5 = 0.4 여섯 번째 데이터 포인트 (오류): 오류율 = 3/6 = 0.5 일곱 번째 데이터 포인트 (정답): 오류율 = 3/7 ≈ 0.429 여덟 번째 데이터 포인트 (정답): 오류율 = 3/8 = 0.375 아홉 번째 데이터 포인트 (오류): 오류율 = 4/9 ≈ 0.444 열 번째 데이터 포인트 (오류): 오류율 = 5/10 = 0.5 결국, 세번째 포인트부터 임계점인 0.3을 초과했지만, 다섯번째 이후부터 오류율이 지속적으로 크게 나타나는것을\n볼 수 있다.\n이렇게, 오류율이 지속적으로 임계점 이상을 초과할 경우 model에 drift가 발생한것으로 간주할 수 있다.\nfrom river import drift ddm = drift.DDM(min_num_instances=30, warning_zone=2, out_control_zone=3) data_stream = [0, 1, 0, 0, 1, 1, 0, 1, 1, 1] # 예시 데이터 스트림의 결과 for i, value in enumerate(data_stream): ddm.update(value) # DDM에 결과 업데이트 if ddm.change_detected: print(f\u0026#34;Change detected index {i}\u0026#34;) elif ddm.warning_detected: print(f\u0026#34;Drift warning index {i}\u0026#34;) 마치며 # 오늘은 concept drift를 예방하고 탐지하기 위한 여러가지 방법론을 정리해보았다.\ndrift에 대응하기 위한, 생각보다 다양한 방법이 있다는것을 알게되었고,\n실제 모델 구축 및 배포시 Drift로 인한 모델 성능저하를 빠르게 탐지하기 위해\n적절한 모니터링 및 탐지 프로세스를 구축해야한다는 점을 이해하게 되었다.\n다음에는 위 탐지 방법론들을 어떻게 프로세스로 녹여낼것인지에 대해 고민하고 정리해보도록 하자.\nReference # [1] Blog\nhttps://calmmimiforest.tistory.com/120\n[[Model Drift] Model Drift에 대한 A to Z # 2. Detection 방법과 Handling 방법\n앞서 Model Drift 개념과 유형에 대해서 알아보았습니다. 이번 글에서는 Model Drift를 감지하는 방법과 Model Drift를 방지하고 해결하는 방법에 대해서 정리해 보겠습니다. Model Drift Detection 방법 첫 번\ncalmmimiforest.tistory.com](https://calmmimiforest.tistory.com/120)\nhttps://danbi-ncsoft.github.io/works/2019/02/07/churn_prediction_in_practice-2.html\n[실전 이탈 예측 모델링을 위한 세 가지 고려 사항 #2\ndanbi-ncsoft.github.io](https://danbi-ncsoft.github.io/works/2019/02/07/churn_prediction_in_practice-2.html)\n","date":"2023.12.19","externalUrl":null,"permalink":"/posts/71/","section":"글","summary":"","title":"Drift를 감지하는 방법 (2) \bConcept Drift Detection (+ADWIN, Page-Hinkley Test, CUSUM, DDM)","type":"posts"},{"content":"","date":"2023.12.19","externalUrl":null,"permalink":"/tags/model-drift/","section":"Tags","summary":"","title":"Model Drift","type":"tags"},{"content":"","date":"2023.12.19","externalUrl":null,"permalink":"/tags/page-hinkley-test/","section":"Tags","summary":"","title":"Page-Hinkley Test","type":"tags"},{"content":"","date":"2023.12.17","externalUrl":null,"permalink":"/tags/data-drift/","section":"Tags","summary":"","title":"Data Drift","type":"tags"},{"content":"지난 포스팅에서는 Model Drift의 개념과 발생원인에 대해서 정리해보았다.\n\u0026gt; Model Drift의 개념과 원인 (+ Data Drift, Label Drift, Concept Drift)\nhttps://familia-89.tistory.com/69\n잠깐 리마인드해보자면,\nmodel drift가 발생했을때, 가장 대표적인 원인으로는 data drift, label drift, concept drift를 꼽을 수 있었다.\ndata drift는 학습당시의 데이터분포와 프로덕트단에서 input되는 데이터의 분포가 상이할 때 발생하는 성능 저하 현상이다.\nlabel drift는 target데이터 분포가 학습당시와 프로덕트 단에서 차이가 있을 경우, 발생하는 성능 저하 현상이다.\n마지막으로, concept drift는 feature와 target간의 관계가, 학습당시와 달라질 경우 발생할 수 있는 성능 저하 현상이다.\n다만, 실제 프로덕트에서 위와 같이 model drift가 발생했을때, 원인을 정확히 정의하기 위해서는 그에 맞는 적절한 검증법을 통한\n실험으로써 drift의 원인을 찾을 수 있다.\n오늘은 Data Drift를 detection할 수 있는 검증방법들을 살펴보자.\n# Data/Label Drift Detection # Statistical Tests(통계적 검정) # : 학습데이터와 프로덕트단에서 input되는 데이터 집합간의 분포차이를 수치화하여 이상여부를 판단하는 방법이다.\n1. Kolmogorov-Smirnov Test(이하 KS Test) # KS Test 검증법은 두 표본내의 각각의 원소(포인트)에 대한 누적분포를 계산한 후, 두 집합내의 동일한 포인트간의 누적분포 수치 차이를 구한 후, 그 중 \u0026lsquo;**최대 누적분포 차이\u0026rsquo;**를 통해, 두개의 표본이 같은 같은 분포를 가지는지를 검증하는 방법이다.\n여기서 *누적분포에 대해서 잠시 알아보자.\n표본 A : [1,2,3,4,5] 표본 B : [2,3,4,5,6] 위와 같은 두개의 표본이 있을때, 각 표본의 누적분포는 아래와 같이 구할 수 있다.\n표본A의 누적분포 1이하의 값의 비율 : 1/5 = 0.2 2이하의 값의 비율 : 2/5 = 0.4 3이하의 값의 비율 : 3/5 = 0.6 4이하의 값의 비율 : 4/5 = 0.8 5이하의 값의 비율 : 5/5 = 1.0 따라서 표본A의 누적분포는 -\u0026gt; [0.2, 0.4, 0.6, 0.8, 1.0]으로 구할 수 있다. 동일한 방법으로 계산한 표본 B의 누적분포는 [0.2, 0.4, 0.6, 0.8, 1.0] 이다. 결국, 분포의 각 포인트별 누적분포를 비교했을때, **\u0026lsquo;최대 누적분포\u0026rsquo;**는 0이므로, 두 분포간의 차이는 없다고 판단할 수 있다.\npython에서는 KS Test를 진행할 수 있는 라이브러리를 제공하고 있다.\nimport numpy as np import scipy.stats as stats # 샘플 데이터 생성 np.random.seed(0) train_set = np.random.normal(0, 1, 1000) # 초기 데이터셋 (정규 분포) product_set = np.random.normal(0.1, 1.1, 1000) # 새로운 데이터셋 (분포 약간 다르게) ks_statistic, p_value = stats.ks_2samp(train_set, product_set) print(f\u0026#34;KS Test Score: {ks_statistic}\u0026#34;) [output] KS Test Score: 0.086 Population Stability Index (PSI) # : PSI 또한 두 집단간 분포의 차이를 통해 계산하는 방식이라는 점은 위 KS Test와 동일하지만, 각 원소의 분포를 비교하는것이\n아닌, 구간별 분포를 비교한다는 부분에 있어 차이점이 있다. 그리고 PSI는 데이터 구간내에서 \u0026lsquo;기대빈도\u0026rsquo;와 \u0026lsquo;실제빈도\u0026rsquo;사이의 차이를\n비율로 표현하여 데이터 집단간의 분포차이를 수치화한다는 점이 특징이다.\n아래 예시를 통해 확인해보자.\n표본 A : [1,2,2,3,4] 표본 B : [2,3,3,4,5] 1단계) 데이터 구간 나누기\n예를들어 위 표본을 아래와 같이 세 구간으로 나눈다.\n구간1 : [1-2] 구간2 : [3-4] 구간3 : [5] 2단계) 구간별 데이터 비율 계산\n표본 A와 표본B의 각 구간에서 데이터 비율을 계산한다.\n표본 A 구간1 : 3/5 (원소 1,2,2가 여기 속한다) 구간2 : 2/5 (원소 3,5가 여기 속한다) 구간3 : 0/5 (해당하는 원소가 없다) 표본 B 구간1 : 1/5 (원소 2가 여기 속한다) 구간2 : 3/5 (원소 3,3,4가 여기 속한다) 구간3 : 1/5 (원소5가 여기 속한다) 3단계) PSI 계산\nPSI는 다음 공식을 사용하여 계산된다.\n위 예시 표본을 통해, PSI를 계산해보자.\n구간 1: (0.2-0.6) x ln(0.2/0.6) 구간 2: (0.6-0.4) x ln(0.6/0.4) 구간 3: (0.2-0) x ln(0.2/(0+ϵ)) * ϵ은 0으로 나누기를 방지하기 위한 작은 수\n* ln : 자연로그\n그럼 이제, PSI를 코드로 구현해보자\nimport numpy as np # 샘플 데이터 생성 np.random.seed(0) train_set = np.random.normal(0, 1, 1000) # 초기 데이터셋 (정규 분포) product_set = np.random.normal(0.1, 1.1, 1000) # 새로운 데이터셋 (분포 약간 다르게) # PSI 계산 함수 def calculate_psi(train_set, product_set, buckets=10): # 구간 분할 breakpoints = np.linspace(np.min([train_set.min(), product_set.min()]), np.max([train_set.max(), product_set.max()]), num=buckets + 1) train_set_counts = np.histogram(train_set, breakpoints)[0] product_set_counts = np.histogram(product_set, breakpoints)[0] # 분포 비율 계산 train_set_ratios = train_set_counts / train_set_counts.sum() product_set_ratios = product_set_counts / product_set_counts.sum() # PSI 계산 psi_value = np.sum((product_set_ratios - train_set_ratios) * np.log((product_set_ratios + 1e-10) / (train_set_ratios + 1e-10))) return psi_value psi_value = calculate_psi(train_set, product_set) print(f\u0026#34;PSI: {psi_value}\u0026#34;) [output] PSI: 0.10168885498870324 어느정도의 PSI 수치가 관찰되었을때, 주의가 필요하거나 데이터분포에 유의미한 변화가 있다고 가정할 수 있을지는\n상황에 따라 경험적, 실험적인 판단이 필요하겠지만,\n보통 0.1~0.2구간에서는 주의가 필요하며, 0.25이상의 차이가 관측될 경우 모델이나 데이터분포에 큰 변화가 있음을 의미하며\n모델 또는 데이터 전략에 수정이 필요할 것으로 판단할 수 있다.\nVisualization (시각화) # 마지막으로, 데이터 분포의 변화를 시각화를 통해 직관적으로 파악하는 방법이다.\n예를들어, 각 Feature의 최대값, 최소값, 평균값, 상관계수 등의 수치를 시간에 흐름에 따른 변화량을 관찰하는 방법이다.\n가장 직관적이고 손쉽게 데이터의 변화 여부를 파악할 수 있다.\n# 마치며 # 오늘은 여러 model drift의 원인 중 Data Drift 또는 Label Drift를 detection할 수 있는 여러 방법들을\n정리해보았다. 간단하게는 feature별 분포의 변화를 통해 데이터 변화를 관측할 수 있었고,\nPSI, KS Test를 통해 조금 더 객관적으로 기준데이터와 관찰데이터의 차이를 수치화할 수 있었다.\n그리고, KS Test의 경우 표본 내 각 값들 사이의 분포를 비교한다면, PSI는 구간 내 데이터의 비교를 통해 차이를 관측하는 방법이므로,\n목적과 상황에 따라 적절히 사용할 수 있도록 해야겠다.\n다음에는 Concept Drift를 detction할 수 있는 검증방법에 대해 정리해보도록 하자.\n# # Reference # 1) Scipy document\nhttps://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.ks_2samp.html\n[scipy.stats.ks_2samp — SciPy v1.11.4 Manual\n[1] Hodges, J.L. Jr., “The Significance Probability of the Smirnov Two-Sample Test,” Arkiv fiur Matematik, 3, No. 43 (1958), 469-86.\ndocs.scipy.org](https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.ks_2samp.html)\n2) Blog\nhttps://www.fiddler.ai/blog/measuring-data-drift-population-stability-index\n[Measuring Data Drift: Population Stability Index | Fiddler AI Blog\nLearn what Population Stability Index (PSI) measure is, its historical usage, and its connection to other mathematical drift measures such as KL divergence.\nwww.fiddler.ai](https://www.fiddler.ai/blog/measuring-data-drift-population-stability-index)\nhttps://roytravel.tistory.com/349\n[[확률/통계] 누적분포함수 (CDF, Cumulative Distribution Function)\n누적분포함수란 확률론에서 주어진 확률분포가 특정 값보다 작거나 같은 확률을 나타내는 함수이다. 이 특정 값이라는 것은 어떤 사건을 의미하므로 누적분포함수는 어떤 사건이 얼마나 많이/\nroytravel.tistory.com](https://roytravel.tistory.com/349)\nhttps://abluesnake.tistory.com/163\n[[모니터링] 2) 드리프트 감지 방법: KS 검정과 PSI\n들어가며 지난 포스트에서 모델 드리프트의 개념과 원인에 대해 알아보았습니다. 시간이 지나면서 모델의 성능이 감소하는 현상을 모델 드리프트라고 하며, 모델 드리프트가 발생하는 주된 이\nabluesnake.tistory.com](https://abluesnake.tistory.com/163)\n","date":"2023.12.17","externalUrl":null,"permalink":"/posts/70/","section":"글","summary":"","title":"Drift를 감지하는 방법 (1) Data Drift Detection","type":"posts"},{"content":"","date":"2023.12.17","externalUrl":null,"permalink":"/tags/ks-test/","section":"Tags","summary":"","title":"Ks Test","type":"tags"},{"content":"","date":"2023.12.17","externalUrl":null,"permalink":"/tags/psi/","section":"Tags","summary":"","title":"Psi","type":"tags"},{"content":"","date":"2023.12.16","externalUrl":null,"permalink":"/tags/label-drift/","section":"Tags","summary":"","title":"Label Drift","type":"tags"},{"content":"머신러닝 모델을 학습하고 배포에 이르렀다는것은 학습과 평가 단계에서 어느정도 서비스에 적용이 가능한 수준의\n성능이 확보되었다는 의미일 것이다.\n따라서 모델을 배포했을 당시에는 의도했던 수준의 퍼포먼스를 확인할 수 있다.\n하지만, 모델이 배포 된 후, 성능하락 속도의 차이는 있겠지만, 시간이 흐름에 따라 모델은 처음에 기대했던 성능에서\n차츰 퍼포먼스가 떨어지게된다.\n이런 현상을 Model drift, Model decay(모델의 쇠퇴), Model staleness(모델의 진부)라고 한다.\n아래에서는 이러한 현상을 model drift로 통일하여 설명하도록 하겠다.\n오늘은 모델의 퍼포먼스가 떨어지는 이유와 해결방법에 대해서 정리해보자.\nhttps://www.projectpro.io/article/model-drift-in-machine-learning/871\nModel Drift ? # model drift란 한마디로, 예측하지 못한 변화로 인하여, 시간이 지나면서 모델의 성능이 저하되는 현상을 의미한다.\n모델은 학습된 모델이 배포된 이후 새로운 데이터에 대해 계속 예측을 수행하게 되는데,\n여러가지 이유로 인해서 최초 배포 이후 지속적으로 성능이 하락하게 된다.\n왜 모델 성능이 하락하는가 ? # 모델의 성능이 하락하는\b 원인들을 하나씩 확인해보자.\n1) Data Drift # : 시간이 지남에 따라 model에 input으로 들어오는 데이터의 분포가 달라짐으로써 발생하는 성능하락\ndata drift란, 모델을 훈련할때의 input 데이터의 통계적 분포와 테스트 또는 프로덕트 환경에서의 input data의 통계적 분포에 차이가 발생하여 성능이 하락하는 현상을 의미한다.\n이 현상은 data drift외에도 Feature drift 또는 Covariate shift라고도 불린다.\n예를들어, 온라인 쇼핑 패턴을 예측하는 모델을 개발했다고 가정하자.\n코로나 이전에는 온라인 쇼핑을 통해 주로 가격대는 중저가에 해당하는 물건이 많이 팔렸고, 판매물품은 주로 의류나 선물용도의 제품들이 많이 팔렸다. 하지만, 코로나19가 유행하며, 외부활동이 힘들어 식료품을 온라인을 이용해서 구매하는 고객들이 많아졌고, 외부 소비활동을 못하는 점에 대한 보상심리로 인해 대형TV와 같이 고가의 가전제품을 구매하는 고객들이 크게 늘었다. 이럴 경우, 이전의 데이터를 통해 학습된 모델은 이러한 변수에 대해 제대로 학습되지 못했기 때문에, 제대로 된 예측결과를 내놓을 수 없게 된다.\n이때문에 시간이 지날수록 여러가지 새로운 변수를 학습하지못한 모델은 점차 성능이 하락할 수 있다.\n2) Label Drift # : 시간이 지남에 따라, Target data의 분포가 달라짐으로써 발생하는 성능하락\nlabel drift란 모델을 훈련할때의 target data의 분포가 달라짐으로써 발생하는 성능하락을 의미한다.\n예를들어, 당뇨병의 발병확률을 예측하는 모델이 있다고 가정하자.\n보통 당뇨는 40대 이후에 발병하는 확률이 매우 높았지만, 최근 사람들이 섭취하는 음식 중 당분이 많이 첨가된 음식들이 너무나 많다.\n그리고 어릴때부터 이런 음식에 많이 노출된 어린아이들이나 청년층들의 당뇨병 발병 연령은 분명히 예전보다 빨라졌을것이다.\n만약 의료 데이터를 학습한 모델이, 과거의 중장년층 이상에서 당뇨가 많이 발생하는 label 분포를 가진 data로 학습되었을 경우 최근의 이런 target 분포 변화를 반영하지 못했을것이므로, 당연히 최근 데이터를 통한 당뇨예측에서는 성능이 하락할것으로 예상할 수 있다.\n3) Concept Drift # : 시간이 지남에 따라 input과 target간의 관계가 변하는 현상으로 발생하는 성능하락\n예를들어, 최근 많이 발생하는 \u0026lsquo;금융 사기 패턴\u0026rsquo;을 학습한 모델이 있다고 가정하자.\n금융 사기는 사기범들의 수법이 정교해지고 다양해짐에 따라, input data와 target data의 관계가 계속해서 변화할 수 있다.\n만약, 금융사기를 탐지하는데 key feature로 활용되던 \u0026lsquo;A\u0026rsquo; feature가 있었는데, 사기범들이 이를 우회하기 위해 다른 방법으로 이상거래를 시도할 경우 \u0026lsquo;A\u0026rsquo; Feature와 target과의 관계는 더 이상 의미를 가지지 못하게 된다.\n또, 사람들에게 적합한 의류를 추천해주는 모델이 있을때, 예전에는 성별은 의상선택에 매우 큰 영향을 끼치는 요소였지만, 현재는 unisex 가 트렌드로 성별을 초월하여 의상을 선택하곤 한다. 이럴 경우 성별이라는 feature가 의상선택에 끼치는 영향이 약화되기때문에 학습된 모델의 성능도 하락할 수 있다.\n4) 기타 # 이 외에도, 만약 모델에 투입되는 전처리 과정에서, 다양한 input데이터의 형태를 고려하지 못한, 또는 또다른 예외 형태의 데이터가 input될 경우, 그리고 이런 모델의 input case 빈도가 높아질 경우에도 모델의 성능이 하락할 수 있다.\n오늘 정리를 통해서 모델의 성능이 하락하는 원인과 사례에 대해서 고민해볼 수 있었다.\n다음에는 모델의 성능을 탐지하고 원인을 분석할 수 있는 방법에 대해 정리해보도록 하겠다.\nRef.\n출처: https://abluesnake.tistory.com/163\n출처: https://engineer-mole.tistory.com/278 [매일 꾸준히, 더 깊이:티스토리]\n","date":"2023.12.16","externalUrl":null,"permalink":"/posts/69/","section":"글","summary":"","title":"Model Drift의 개념과 원인 (+ Data Drift, Label Drift, Concept Drift)","type":"posts"},{"content":"","date":"2023.12.16","externalUrl":null,"permalink":"/tags/target-drift/","section":"Tags","summary":"","title":"Target Drift","type":"tags"},{"content":"","date":"2023.12.16","externalUrl":null,"permalink":"/tags/%EB%AA%A8%EB%8D%B8-%EC%84%B1%EB%8A%A5/","section":"Tags","summary":"","title":"모델 성능","type":"tags"},{"content":"","date":"2023.12.14","externalUrl":null,"permalink":"/tags/machine-learning/","section":"Tags","summary":"","title":"Machine Learning","type":"tags"},{"content":"","date":"2023.12.14","externalUrl":null,"permalink":"/tags/ml-engineer/","section":"Tags","summary":"","title":"Ml Engineer","type":"tags"},{"content":"","date":"2023.12.14","externalUrl":null,"permalink":"/tags/mlops/","section":"Tags","summary":"","title":"Mlops","type":"tags"},{"content":"오늘은 MLOps에 대해서 자세히 정리해보고자 한다.\nMLOps 란? # 먼저 MLOps라는 용어는 Machine Learning Operations, ML과 Ops(Operations)의 합성어로,\n학습한 머신러닝 모델을 안정적이고 효율적으로 배포 및 유지관리하는 것을 목표로 하는 \u0026lsquo;패러다임\u0026rsquo;이다.\n그리고, MLOps Process는 ML과 Operation에 해당하는 영역으로 각각 구분할 수 있다.\nML 단계 : 데이터 수집, 전처리, 모델 구축, 학습, 평가 등 Ops 단계 : 모델 배포, 모니터링, 테스트 등 MLOps는 불과 몇년전부터 주목받기 시작한 기술인데, 이는 ML이 실제 서비스에 적용되고 운영되기 시작하면서\n머신러닝 모델의 지속적 통합, 배포, 학습을 관리할 수 있는 시스템의 필요성을 인식하기 시작했기 때문이다.\n먼저, MLOps에 대해서 세부적으로 알아보기 위해, 머신러닝 모델의 life cycle 내부에서 역할을 이해해보자.\n머신러닝 모델 Life Cycle\n모든 머신러닝 모델은 위의 주기에 따라 개발되고 실제 서비스에 적용되어 비즈니스 목표를 달성하게 된다.\n머신러닝 모델의 Life Cycle을 구체적으로 나열해보면 아래와 같다.\n해결하고자하는 문제의 목표를 정의 필요한 데이터 정의 및 수집 EDA 및 데이터 전처리 모델 설계 학습 및 평가 최적화 배포 모니터링 배포된 모델은 시간이 지남에 따라, 다양한 원인으로 최초 배포시점과 비교해서 성능이 하락한다.\n(성능하락의 원인과 해결방법은 다음 에 포스팅해보겠다.)\n따라서 적절한 시점에 모델에 대한 재학습 및 재배포 과정 또한 수반되어야하는데,\n그때 마다 위 cycle은 모두 빠짐없이 진행되어야하는 과정이다.\n만약, 관리해야할 모델이 하나이고, 재학습 주기를 길게 가져가도 문제가없거나, PoC 수준의 목적을 가진 모델의 경우,\n위의 모델 개발, 검증, 배포, 모니터링과 같은 과정들을 수동으로 처리해도 큰 문제가 발생하지 않는다.\n하지만, 서비스의 규모가 커지게 되면서, 적용되는 모델이 많아지고, 재학습 주기가 짧을수록 모든 과정들을 수동으로 처리하는것이 매우 어려워진다. 이 문제를 해결하기 위해 필요한 패러다임이 MLOps이다.\nMLOps의 목표 # 그렇다면, 구체적으로 MLOps로 얻을 수 있는 이점은 어떤점이 있을까?\nMLOps의 목표는 크게 아래 6가지로 요약할 수 있다.\n1) 배포 및 자동화 # 머신러닝 모델을 실제 운영 환경에 배포하고, 이 과정을 자동화하여 모델의 배포와 업데이트가 빠르고 효율적으로 이루어질 수 있도록 한다.\n2) 거버넌스 및 규정 준수 # 머신러닝 모델이 법적, 윤리적 기준 및 기업 정책 등 을 준수하도록 관리한다. 이는 데이터 보호, 개인 정보 보호 및 모델의 공정성을 포함할 수 있는데, 모델이 지켜야할 규칙을 정의하고 이를 관리하는 역할을 의미한다.\n3) 확장성 # 머신러닝 시스템이 다양한 데이터 크기, 사용자 수 및 복잡성에 따라 적절하게 확장될 수 있도록 지원한다. 이를 통해 시스템의 성능과 안정성을 유지하는 역할을 한다.\n4) 협업 # 데이터사이언티스트와 소프트웨어 엔지니어, 서비스 운영 팀 간의 협업을 촉진하고, 효율적인 작업 Flow를 지원하며 커뮤니케이션을 가능하게 한다.\n5) 모니터링 및 관리 # 배포된 머신러닝 모델의 성능을 지속적으로 모니터링하고 관리하여 문제가 발생할 경우 적시에 대응할 수 있도록 한다. 이는 서비스 배포 이후에도 모델의 정확도와 신뢰성을 유지하는 데 필수적이다.\n6) 재현성 # 데이터 전처리 및 모델 학습 과정을 재현할 수 있도록 관리하여, 결과의 일관성과 검증 가능성을 보장할 수 있게끔한다. 서비스 운영관점에서 모델의 정확성과 신뢰성을 위해 매우 중요하다.\n모델을 운용하는 환경과 상황은 모두 다르겠지만, MLOps는 위 목표를 달성하기 위해 적절한 방식으로 구현되어야한다.\nMLOps 구축 단계 # MLOps는 모델 개발 및 배포, 모니터링 등 머신러닝 모델 Life cycle의 여러단계를 아우르는 개념으로,\n구축의 범위, 정도에 따라 MLOps의 성숙도, 즉 수준을 가늠할 수 있다.\nGoogle에서는 가장 아래 단계인 프로세스 수동 적용 단계부터, ML과 CI/CD Pipeline을 모두 자동화하는 단계까지 총 3가지의 단계로\n성숙도의 수준에 대한 평가 기준을 제시하고 있어, 현재 구축 수준을 평가하고, 이후 단계로의 개선점으로 참고할 수 있다.\nMLOps 수준 0: 수동 프로세스 MLOps 수준 1: ML pipeline 자동화 MLOps 수준 2: CI/CD Pipeline 자동화 ‍\nMLOps LV 0\nMLOps 수준 0 프로세스에서는 데이터 분석과 데이터 준비, 모델 학습 및 검증을 포함한 모든 단계가 수동으로 이루어진다.\n각 단계를 수동으로 실행하고, 한 단계에서 다음 단계로 전환하는 것 역시 수동으로 이루어진다.\n이 단계에서는 ML과 운영이 단절되어 있으며, 이로 인해 학습과 서비스 간의 간극이 발생하기도 하는데, 학습 당시의 성능과 서비스단에서의 실제 성능에서 차이가 발생하고 이를 빠르게 해결할 수 없는 구조를 가진다.\n일반적으로 새 모델 버전은 1년에 두어 번만 배포하며(당연히 해당 단계에서는 재학습 및 배포가 자주 이루어지는것이 어려울것이고, 현재 팀 내 대부분의 모델들이 Lv0의 상태에 해당한다.) 프로덕션 환경에서는 권장되지 않는 성숙도이다.\nMLOps LV 1\nMLOps 레벨 1은 머신러닝 실험 단계를 자동화하여 빠르게 실험을 반복하고, 전체 pipeline을 프로덕션으로 전환할 수 있는 준비 상태를 의미한다. 이 레벨에는 몇 가지 특성이 있다.\n실험-운영 대칭성: 개발 또는 실험 환경에서 사용되는 pipeline 구현이 사전 프로덕션 및 프로덕션 환경에서도 사용된다. 컴포넌트 및 pipeline을 위한 모듈화된 코드: 컴포넌트의 소스 코드는 모듈화되어있거나 컨테이너화된 형태를 보인다. 프로덕션 환경의 ML pipeline은 새로운 데이터로 학습된 새로운 모델으로 실제 서비스를 지속적으로 제공한다. 모델 배포 단계는 자동화된 상태. 레벨 1에서는 전체 학습 pipeline을 배포하고, 이 pipeline은 자동으로 반복적으로 실행되어 학습된 모델을 서비스에 제공한다. 정리하면, MLOps 레벨 1은 머신러닝 학습 및 평가 과정을 pipeline을 통해 자동화하여 손쉽게 반복할 수 있다.\n또한 프로덕션 환경에서 모델을 학습하고, 배포하는 작업을 더 쉽게 수행할 수 있게 된다.\n‍\nMLOps LV 2\nhttps://cloud.google.com/architecture/mlops-continuous-delivery-and-automation-pipelines-in-machine-learning?hl=ko\n서비스단에서 ML Pipeline 전체를 빠르고 안정적으로 업데이트하기 위해서는 자동화된 CI/CD 시스템이 필요하며,\n이 자동화된 시스템을 사용하면 데이터사이언티스트가 ML모델을 개발함에 있어 특성 추출, 모델 아키텍처, 하이퍼파라미터에 대한\n새로운 아이디어를 빠르게 반영할 수 있게 된다.\n이 단계의 MLOps Pipeline 정리하면 아래와 같다.\n개발 및 실험: 새로운 ML 알고리즘과 모델링 기법을 반복적으로 시도할 수 있으며, 이 단계의 결과물은 ML pipeline 단계의 소스 코드이며, 소스 리포지토리에 푸시된다. pipeline 지속적 통합(CI): 소스 코드를 빌드하고 다양한 테스트를 실행한다. 이후 단계에서 배포할 pipeline 구성 요소(패키지, 실행 파일 및 아티팩트)을 목표로 하게된다. pipeline 지속적 배포(CD): CI 단계에서 생성된 아티팩트를 대상 환경에 배포하고, 모델의 새로운 구현이 포함된 pipeline을 출력한다. 자동 트리거링: 일정에 따라 또는 트리거에 대한 응답으로 프로덕션 환경에서 pipeline을 자동으로 실행한다. ‍학습된 모델을 배포한 후에는 라이브 데이터를 기반으로 모델 성능에 대한 통계를 수집해야 하고,\n모니터링 결과를 통해, pipeline을 실행하거나 새로운 실험 주기를 찾을 수 있는 계기를 얻을 수 있다.\n위 Flow를 참고하여, 지속적 통합과 지속적 배포 시스템 설정을 통해 새로운 ML pipeline을 자동으로 테스트하고 배포할 수 있게된다.\n-\u0026gt; Lv2수준의 MLOps Process는 각 단계별로 목적과 구현방법에 대해 추후 좀 더 구체적으로 정리할 필요가 있을 것 같다.\n현재 팀내에서 MLOps 프로세스를 구현하기 전 고민해야 할 부분은? # 머신러닝 모델을 개발하고 운영하는 모든 조직은 적절한 MLOps 시스템을 구축하는것에 대해 고민해야한다.\n위 Google에서 제시한 MLOps 성숙도를 기준으로 2단계에 해당하는 수준의 MLOps시스템을 구축하는것이 이상적이긴 하나,\nPoC 수준의 모델, 잦은 배포가 필요하지않은 모델, 사용성이 낮은 프로덕트에서 활용되는 모델과 같은 케이스에서 굳이 2단계에 해당하는 수준의 MLOps 시스템을 구축하는것은 적합하지 않을 것이다.\n현재 팀내에서 운영되고 있는 모델 중 하루 호출 건수가 수백만건 이상 발생하는 모델에 대해 신규 데이터를 통해 재학습 한 후 수동 배포한 경험이 있다. 이후, 모델 정확도에 대한 로그를 쌓고, 해당 로그를 매일 수동으로 확인해야하는 불편함이 있었다. 그리고 배포 이후 3개월이 채 지나지 않아, 모델의 정확도가 20%이상 하락했음에도 불구하고, 수동 모델 학습 및 배포방식으로 개발되었기 때문에 빠르게 재배포를 진행할 수 없었다. 실제 운영중인 프로덕트에서 MLOps Pipeline이 구축되어있지 않을 경우, 성능을 보장할 수 없고, 빠른 개선 또한 어렵다는 점을 알 수 있었고, 자동화 되지않은 프로세스로 인해서 계속해서 불필요한 리소스가 투입되는것을 보며, 프로덕트에 모델을 적용하는 초기 단계부터 MLOps pipeline 구축에 대한 구체적인 계획과 실행이 필요하다는 것을 느끼게 되었\u001d다.\nRef.\nhttps://www.thedatahunt.com/trend-insight/guide-mlops[MLOps 란? - 정의, 구성요소, 레벨별 프로세스, Use case\nMLOps - Machine Learning Operations는 머신러닝 모델의 안정적이고 효율적인 배포 및 유지 관리를 목표로, 머신러닝 시스템을 위한 지속적인 통합/배포/학습을 구현하고 자동화하는 기술을 의미합니다.\nwww.thedatahunt.com](https://www.thedatahunt.com/trend-insight/guide-mlops)\nhttps://www.thedatahunt.com/trend-insight/guide-mlops\n","date":"2023.12.14","externalUrl":null,"permalink":"/posts/68/","section":"글","summary":"","title":"MLOps란?","type":"posts"},{"content":"컴퓨터 시스템에서 캐시(cache)는 데이터 처리 속도를 향상시키는 중요한 역할을 한다.\n오늘은 소프트캐시와 하드캐시의 차이점과 각각의 역할과 목적을 정리해보려한다.\n하드캐시 (Hardware Cache)\n하드캐시는 CPU 또는 GPU 내부에 위치한 빠른 메모리 공간인데, 속도가 느린 주 기억장치(RAM)와 빠른 CPU/GPU 사이의 속도 차이를 극복하기 위해 사용된다. 하드캐시는 자주 접근하는 데이터와 명령어를 저장하여, 프로세서가 빠르게 접근할 수 있도록 도와\b주며, 당연하지만, 하드캐시의 경우 내부가 가득찼을때 또는 새로운 데이터가 필요할때, 내부 매커니즘에 따라 삭제를 진행한다. 가장 대표적으로 LRU(Least Recently Used)와 같은 알고리즘을 사용한다.\n특징:\n매우 빠른 접근 속도.\n용량이 제한적.\n자동 관리되며 사용자 개입 불필요.\n역할:\n시스템의 전반적인 성능 향상.\n데이터 처리 효율성 증가.\n소프트캐시 (Software Cache)\n소프트캐시는 소프트웨어 레벨에서 구현된 캐시로, 하드 디스크나 SSD에 위치하며, 이는 주로 대규모 데이터셋, 다운로드한 파일, 웹 콘텐츠 등을 임시로 저장하는 데 사용된다.\n특징:\n사용자가 설정 및 관리할 수 있음.\n더 큰 용량을 제공하지만, 하드웨어 캐시보다 접근 속도가 느림.\n네트워크 대역폭 및 시간 절약에 도움.\n역할:\n반복적인 데이터 다운로드 최소화.\n오프라인 데이터 접근 향상.\n성능 최적화 및 사용자 경험 개선.\n소프트웨어 캐시의 예시: SentenceTransformer\n딥러닝 모델으 활용함에 있어, soft cache를 사용하는 예시를 들자면,\nSentenceTransformer와 같은 HuggingFace의 모델을 로드할때를 예로 들 수 있는데,\n자연어 처리 분야에서 사용되는 사전 학습된 모델을 캐시하는 데 소프트웨어 캐시를 사용한다.\n이는 문장 임베딩을 생성하는 데 사용되는 도구로, 다음과 같이 작동한다.\nModel Load:\nSentenceTransformer는 SentenceTransformer(\u0026lsquo;모델명\u0026rsquo;)을 통해 Hugging Face의 모델 허브에서 사전 학습된 모델을 불러온다.\n\bCache 사용:\n처음 모델을 다운로드할 때, 모델 데이터는 소프트웨어 캐시 디렉토리에 저장하는데,\n이후 모델을 사용할 때, 캐시된 데이터를 불러와 시간과 네트워크 자원을 절약할 수 있다.\nCache 관리:\n사용자는 환경 변수나 cache_folder 옵션을 통해 캐시 디렉토리를 설정할 수 있고,\n이를 통해 사용자는 캐시 데이터의 저장 위치를 제어하고 관리할 수 있다.\n위 내용을 정리 하면, 하드캐시와 소프트캐시는 각기 다른 방식으로 컴퓨터 시스템의 성능을 최적화한다. 하드캐시는 CPU/GPU의 빠른 데이터 처리를 위해, 소프트캐시는 대용량 데이터의 효율적 관리를 위해 사용된다. SentenceTransformer 같은 도구를 사용할 때 소프트캐시의 역할은 데이터 접근성과 효율성을 크게 향상시키며, 이러한 캐시 메커니즘의 이해는 데이터 처리와 관련된 다양한 작업에서 중요한 역할을 하게된다.\n","date":"2023.11.27","externalUrl":null,"permalink":"/posts/67/","section":"글","summary":"","title":"[Cache]\bSoft Cache \u0026 Hard Cache","type":"posts"},{"content":"","date":"2023.11.27","externalUrl":null,"permalink":"/tags/cache/","section":"Tags","summary":"","title":"Cache","type":"tags"},{"content":"","date":"2023.11.27","externalUrl":null,"permalink":"/tags/hard-cache/","section":"Tags","summary":"","title":"Hard Cache","type":"tags"},{"content":"","date":"2023.11.27","externalUrl":null,"permalink":"/tags/soft-cache/","section":"Tags","summary":"","title":"Soft Cache","type":"tags"},{"content":"","date":"2023.11.27","externalUrl":null,"permalink":"/tags/%EC%86%8C%ED%94%84%ED%8A%B8%EC%BA%90%EC%89%AC/","section":"Tags","summary":"","title":"소프트캐쉬","type":"tags"},{"content":"","date":"2023.11.27","externalUrl":null,"permalink":"/tags/%EC%BA%90%EC%8B%9C/","section":"Tags","summary":"","title":"캐시","type":"tags"},{"content":"","date":"2023.11.27","externalUrl":null,"permalink":"/tags/%ED%95%98%EB%93%9C%EC%BA%90%EC%89%AC/","section":"Tags","summary":"","title":"하드캐쉬","type":"tags"},{"content":" 1) PS ? # ps 명령어는 UNIX 계열 운영 체제에서 현재 실행 중인 프로세스 목록을 표시하는 데 사용되며, 다양한 옵션을 가지고 있다.\n그 중 ps -ef와 ps aux는 이 옵션들 중 가장 널리 사용되는 두 가지 형태이다.\n2) 세부 옵션 비교 # ps -ef와 ps aux 모두 시스템에서 실행 중인 프로세스에 대한 상세한 정보를 제공하지만,\n출력 형식과 표시되는 정보에 약간의 차이가 있어, 용도와 목적에 맞게 사용할 수 있다.\nps -ef\n-e 옵션 : 모든 프로세스를 나열한다. -f 옵션 : 전체 포맷을 사용하며. 각 프로세스에 대한 상세한 정보를 보여준다. 출력 내용 UID(사용자 ID) PID(프로세스 ID) PPID(부모 프로세스 ID) C(프로세서 사용량) STIME(시작 시간) TTY(터미널 타입) TIME(총 CPU 사용 시간) CMD(실행 명령어) 등을 이 형식은 주로 System V 계열의 UNIX 시스템에서 사용된다.\nps aux\na 옵션 : 다른 사용자의 프로세스를 포함한 모든 프로세스를 나열한다. u 옵션 : 프로세스의 소유자에 대한 정보를 나타내는 사용자 지향 포맷을 사용한다. x 옵션 : 터미널에 연결되지 않은 프로세스도 나열한다. 출력 내용 USER(소유자) PID(프로세스 ID) %CPU(CPU 사용량 비율) %MEM(메모리 사용량 비율) VSZ(가상 메모리 사이즈) RSS(실제 메모리 사이즈) TTY(터미널 타입) STAT(프로세스 상태) START(프로세스 시작 시간) TIME(총 CPU 사용 시간) COMMAND(실행 명령어) 이 형식은 주로 BSD 계열의 UNIX 시스템에서 사용되며, Linux 시스템에서도 널리 사용되고 있다.\nps -ef 와 ps aux의 활용상 차이점\nps aux는 %CPU와 %MEM을 표시하는 반면, ps -ef는 이러한 정보를 보여주지 않는다. 그리고, ps aux는 사용자 이름을 USER 컬럼으로 보여주는 반면, ps -ef는 UID를 보여주는 부분에 차이가 있다. 출력 포맷과 컬럼의 배열 순서가 다르다. 결국, 두 명령어 중 어느 것을 사용할지는 사용자의 선호도와 필요한 정보에 따라 결정된다. 일반적으로 ps aux는 시스템의 CPU와 메모리 사용 상황을 더 자세히 보고 싶을 때 유용하며, ps -ef는 프로세스 간의 관계(부모-자식)를 더 자세히 파악하고 싶을 때 사용하면 좋을 것이다.\n","date":"2023.11.09","externalUrl":null,"permalink":"/posts/65/","section":"글","summary":"","title":"[Linux] 프로세스 확인 명령어 옵션 비교 (ps -ef / ps aux)","type":"posts"},{"content":"","date":"2023.11.09","externalUrl":null,"permalink":"/tags/ef/","section":"Tags","summary":"","title":"Ef","type":"tags"},{"content":"","date":"2023.11.09","externalUrl":null,"permalink":"/tags/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%B6%9C%EB%A0%A5/","section":"Tags","summary":"","title":"프로세스출력","type":"tags"},{"content":"","date":"2023.11.09","externalUrl":null,"permalink":"/tags/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%ED%99%95%EC%9D%B8/","section":"Tags","summary":"","title":"프로세스확인","type":"tags"},{"content":" top을 통해 살펴보는 프로세스 정보들 # top # 시스템의 상태를 전반적으로 가장 빠르게 파악 가능(CPU, Memory, Process) 옵션 없이 입력하면 interval 간격(기본 3초)으로 화면을 갱신하며 정보를 보여줌 top 실행 전 옵션 순간의 정보를 확인하려면 -b 옵션 추가(batch 모드) -n : top 실행 주기 설정(반복 횟수) top 실행 후 명령어 shift + p : CPU 사용률 내림차순 shit + m : 메모리 사용률 내림차순 shift + t : 프로세스가 돌아가고 있는 시간 순 k : kill. k 입력 후 PID 번호 작성. signal은 9 f : sort field 선택 화면 -\u0026gt; q 누르면 RES순으로 정렬 a : 메모리 사용량에 따라 정렬 b : Batch 모드로 작동 1 : CPU Core별로 사용량 보여줌 ps와 top의 차이점 ps는 ps한 시점에 proc에서 검색한 cpu 사용량 top은 proc에서 일정 주기로 합산해 cpu 사용율 출력 top -b -n 1 3:58 : 3시간 58분 전에 서버가 구동 load average : 현재 시스템이 얼마나 일을 하는지를 나타냄. 3개의 숫자는 1분, 5분, 15분 간의 평균 실행/대기 중인 프로세스의 수. CPU 코어수 보다 적으면 문제 없음 Tasks : 프로세스 개수 KiB Mem, Swap : 각 메모리의 사용량 PR : 실행 우선순위 VIRT, RES, SHR : 메모리 사용량 =\u0026gt; 누수 check 가능 S : 프로세스 상태(작업중, I/O 대기, 유휴 상태 등) # VIRT, RES, SHR # 현재 프로세스가 사용하고 있는 메모리 VIRT 프로세스가 사용하고 있는 virtual memory의 전체 용량 프로세스에 할당된 가상 메모리 전체 SWAP + RES RES 현재 프로세스가 사용하고 있는 물리 메모리의 양 실제로 메모리에 올려서 사용하고 있는 물리 메모리 실제로 메모리를 쓰고 있는 RES가 핵심! SHR 다른 프로세스와 공유하고 있는 shared memory의 양 예시로 라이브러리를 들 수 있음. 대부분의 리눅스 프로세스는 glibc라는 라이브러리를 참고하기에 이런 라이브러리를 공유 메모리에 올려서 사용 # Memory Commit # 프로세스가 커널에게 필요한 메모리를 요청하면 커널은 프로세스에 메모리 영역을 주고 실제로 할당은 하지 않지만 해당 영역을 프로세스에게 주었다는 것을 저장해둠 이런 과정을 Memory commit이라 부름 왜 커널은 프로세스의 메모리 요청에 따라 즉시 할당하지 않고 Memory Commit과 같은 기술을 사용해 요청을 지연시킬까? fork()와 같은 새로운 프로세스를 만들기 위한 콜을 처리해야 하기 때문 fork() 시스템 콜을 사용하면 커널은 실행중인 프로세스와 똑같은 프로세스를 하나 더 만들고, exec() 시스템 콜을 통해 다른 프로세스로 변함. 이 때 확보한 메모리가 쓸모 없어질 수 있음 COW(Copy-On-Write) 기법을 통해 복사된 메모리 영역에 실제 쓰기 작업이 발생한 후 실질적인 메모리 할당을 진행 # 프로세스 상태 # 참고글 : Load Average에 관하여 SHR 옆에 있는 S 항목으로 볼 수 있음 D : Uninterruptiable sleep. 디스크 혹은 네트워크 I/O를 대기 R : 실행 중(CPU 자원을 소모) S : Sleeping 상태, 요청한 리소스를 즉시 사용 가능 T : Traced or Stopped. 보통의 시스템에서 자주 볼 수 없는 상태 Z : zombie. 부모 프로세스가 죽은 자식 프로세스 ","date":"2023.11.08","externalUrl":null,"permalink":"/posts/64/","section":"글","summary":"","title":"[Linux] Top 관련 명령어 정리","type":"posts"},{"content":"","date":"2023.11.08","externalUrl":null,"permalink":"/tags/top/","section":"Tags","summary":"","title":"Top","type":"tags"},{"content":"이번달은 서비스 진행 측면이나, 당뇨CDSS연구과제 그리고, 파트별 학습 계획도 순조롭게 진행되고 있어,\n업무에 대한 피드백보다, 개인적인 시간관리 및 생활방식과 습관에 대해서 돌아보는 시간을 가져보려한다.\n시간관리\n시간관리를 잘한다라는 개념은 사람마다 다를 것 같다.\n좋은 시간관리라는 워딩 자체가 시간에 대한 평가가 포함되어 있고, 사람의 가치관에 따라 평가는 달라진다.\n그렇기 때문에, 시간관리를 계획하기 전에 나에게 좋은 시간은 무엇인지 먼저 생각해봐야할 것 같다.\n회사에서 주어지는 시간에 할당된 업무를 집중도있게 빠르게 수행하고, 그 과정에서 스스로의 성장을 가져갈 수 있는 밀도 있는 시간을 보내었을때 좋은 시간을 보내었다 라고 평가할 수 있겠다.\n일이 침범할 수 없는 가족과 함께하는 고정적인 시간이 필요하고, 물리적 시간만 함께 보내는 것이 아니라, 충분히 대화하고 집중할 수 있는 시간을 가졌을때, 가족과 좋은 시간을 보내었다라고 평가할 수 있다.\n매일 30분이상 운동하며, 에너지를 해소하는 시간을 가졌을때 좀 더 건강한 시간을 보내었다라고 평가할 수 있다.\n그렇다면, 내 하루 시간에 위 항목들을 어떻게 갈아 넣어야 할까\n먼저 24시간은 개선이 가능한 **\u0026ldquo;관리가능시간\u0026rdquo;**과 출근준비 시간처럼 추가 활용이 어려운 **\u0026ldquo;관리불가시간\u0026rdquo;**으로 나눌 수 있는데,\n관리가능시간 중 가장먼저, 퇴근 후 집에서의 시간을 활용해 2번과 3번을 꼭 지키려고한다.\n물론 퇴근후에도 자기개발을 하는 다른 사람들을 생각해보면 혼자 뒤쳐지는 것 같아, 불안해질때까있다.\n다만, 퇴근 후 시간은 유동성이 크고, 컨디션도 많이 다운된 상태이기 때문에 일정하게 집중된 시간을 할애하기 어렵고,\n또 유지하기도 힘들다.\n그렇기 때문에, 퇴근 후에도 어설프게 자기개발 또는 업무에 시간을 할당할 경우,\n결국 휴식도, 자기계발도 얻지 못할 가능성이 높다고 생각한다.\n따라서, 퇴근 후 집에서는 가족과 보내며 휴식하는 시간으로 보내고,\n자기개발은 그 외 시간을 밀도있게 활용하는 것이 나에게는 더 적합한 시간관리라고 결론지었다.\n그리고 나머지 활용가능한 **\u0026lsquo;관리가능시간\u0026rsquo;**은 한번에 활용 가능한 시간의 양을 기준으로\n**\u0026lsquo;틈새시간\u0026rsquo;**과 **\u0026lsquo;주력시간\u0026rsquo;**으로 나누어 활용방법을 모색해보자.\n내 틈새시간은 하루에 4번 정도 생긴다.\n1번 : 07:20 ~ 08:30 출근길 ▶️ 1시간10분\n2번 : 12:25 ~ 13:00 점심휴식 ▶️ 35분\n3번: 18:40 ~ 20:00(유동적) 퇴근길 ▶️ 1시간20분\n4번: 24:00취침 전 ▶️ 약 30분1시간 # ▶️ 총 3시간 35분\n🔶 그동안 왜 번번히 틈새시간을 허투루 보냈는가 # 1) 출퇴근길\n출, 퇴근길에 피곤해 죽겠는데, 자기개발하는건 지루하고, 자극적인 유튜브, SNS에 더 쉽게 손이 갔다.\n자극적인 SNS, 유튜브를 시청하고 나면 남는건 1도없고, 이동시간 동안 피곤하기만 배로 피곤해졌다.\n2) 점심휴식\n보통 부모님께 연락드리고, 유튜브에 할애, 약간의 낮잠\n3) 취침 전\n기본 30분이상 유튜브를 시청하지 않을 경우, 뭔가 나에게 주는 보상이 없는 것 같아 억울한 마음이 들었고\n꼬박꼬박 취침 전 30분~1시간을 핸드폰을 보다가 잠이든다.\n결국, 잔상이 남아 숙면에도 도움이 되지 않았고, 피곤함이 다음날로 이어진다.\n🔶 어떻게 바꿀것인가 # 1) 출퇴근길\n네이버, 인스타 눈팅은 쉽게 끊을 자신이 있는데, 유튜브가 정말 어렵다.\n특히 유튜브로 헬스유튜버 보는데 어마어마한 시간을 보낸다.\n나한테 이익이 있나, 다시 한번 깊게 고민해봤다.\n운동 유튜버 본다고 내가 그 몸될것도 아니고, 대리만족보다는 집에가서 내 운동에만 집중하자고 되뇌어본다.\n스스로에게 절제를 요구하는것보다 아예 단절하는것이 확실하기때문에, 유튜브는 우선 삭제하는것이 좋겠다.\n과거에 앱 삭제하고나서 브라우저로 검색해 들어가 보던 짓을 했는데, 이번엔 정말 그렇게는 하지 않고 바꿔보겠다.\n유튜브는 맥북으로 회사에서 노래들을때만 사용해보겠다고 다짐해본다. 제발\u0026hellip;\n2) 취침 전\n핸드폰을 가지고 침대로 들어가지말고, 그냥 충전을 꽃아버리고, 손을 가볍게 침대로 들어가자.\n하루동안 있었던 일 한번 되뇌어 보면서 잠에들자.\n🔶 틈새시간을 어떤것으로 채울것인가 # 1) 출퇴근 길\n사람이 하루종일 하는 모든 행동에는 \u0026lsquo;난이도\u0026rsquo;가 있다.\n난이도가 높은 행동에는 몰입을 필요로하고, 이 몰입에는 분명히 시간이 소요된다.\n틈새시간은 짧기때문에, 난이도가 높은 계획을 배치할 경우, 실행력이 떨어질 수 있다.\n그럼, 틈새시간엔 뭘해야할까?..\n매달 밀리에 서재를 구독중인데, 책을 읽자.\n다만, 매번 딱딱한 내용을 보다보니, 자극적인 유튜브로 돌아가는것을 반복했는데,\n유익성 보다는 쉽게 잡히는 책 위주로 읽으면서, 먼저 \u0026lsquo;틈새시간에는 책을 읽는다\u0026rsquo;라는 습관이 어느정도 자리잡히면\n실용적인 책으로 넘어가도록 해봐야겠다.\n다음은 \u0026lsquo;주력시간\u0026rsquo;이다.\n🔶 업무시간\u001c 중 긴 시간동안 몰입하는 것이 힘든 이유는? # 업무시간에는 학교다닐때 공부하던 습관이 좀 많이 나타나는 것 같은데,\n난이도가 있는 업무일수록 미루는 습관이 많다.\n고민이 꽤 필요한 업무일 경우, 집중도는 낮아지고 이때, 스트레스를 회피하기 위해 핸드폰을 찾거나,\n쉽고 우선순위가 낮은, 약간은 쓸데없는일을 먼저 집어드는 경향이 있는데,\n스스로 스트레스로 부터 도피하려는 반응같다.\n이렇게 몇가지 업무를 왔다갔다 하다보면 시간이 순식간에 지나가고 업무 진도가 잘 나가지\n않는 경우가 많이 생긴다.\n사실 이 주력시간만 밀도있게 집중해도, 업무 및 자기계발에 충분한 시간이 될 수 있다고 생각한다.\n🔶해결방법은? # 우선 몇가지 방법을 시도해보고자 한다.\n먼저, 시간의 범위를 두고 그 범위내에서는 단, 1가지에만 집중 할 수 있도록 해보자.\n다른 업무나 행동으로 이어질 상황을 원천적으로 제한하자.\n그리고, 집중하는 term을 짧게 (20분 정도?) 가져가고, 반응을 보고 조금씩 늘려가보자.\n우선 너무 많이 시도하지말고, 8월에는 위 두가지만 지켜보려 노력하며,\n시간의 밀도를 높이는데 최선을 다해보자.\n","date":"2023.08.02","externalUrl":null,"permalink":"/posts/62/","section":"글","summary":"","title":"2023.07월 회고","type":"posts"},{"content":"","date":"2023.08.02","externalUrl":null,"permalink":"/categories/retrospective/","section":"Categories","summary":"","title":"Retrospective","type":"categories"},{"content":"1. GPU가 사용 가능한 상태인지 확인\nimport os os.environ[\u0026#39;CUDA_DEVICE_ORDER\u0026#39;]=\u0026#34;PCI_BUS_ID\u0026#34; os.environ[\u0026#39;CUDA_VISIBLE_DEVICES\u0026#39;]=\u0026#39;0\u0026#39; device = torch.device(\u0026#39;cuda\u0026#39; if torch.cuda.is_available() else \u0026#39;cpu\u0026#39;) print(\u0026#39;Device:\u0026#39;, device) # print(\u0026#39;Current cuda device:\u0026#39;, torch.cuda.current_device()) print(\u0026#39;Count of using GPUs:\u0026#39;, torch.cuda.device_count()) [Out]\n-\u0026gt; 사용불가한 상태라면, \u0026lsquo;CPU\u0026rsquo;, Count of using GPUs : \u0026lsquo;0\u0026rsquo;으로 출력된다. 지금은 이상태를 가정하겠다.\n2. 해당 서버 또는 컨테이너의 shell에서 CUDA버전을 확인\nCUDA버전과 torch관련 라이브러리의 버전이 모두 호환되어야 GPU사용이 \u0026lsquo;True\u0026rsquo;로 출력된다. #서버접속 ssh -p 2023 root@10.80.179.4 #cuda버전 확인(상단에 \u0026#39;CUDA 10.1\u0026#39; 과 같은 형태로 노출) watch -d -n 0.5 nvidia-smi -\u0026gt; 정정 : 위 watch -d -n .5 nvidia-smi에서 출력되는 CUDA Version은 설치된 NVIDIA 드라이버에서 권장하는 CUDA버전을 의미한다. 설치된 CUDA버전과는 관계없다.\n-\u0026gt; 정확한 현재 cuda버전을 확인하려면, **\u0026rsquo;nvcc \u0026ndash;version\u0026rsquo;**을 통해 현재 release된 cuda버전을 확인할 수 있다.\n3. 해당 CUDA버전과 호환되는 torch버전 확인\ncuda버전을 확인해서 가장 최신버전의 torch, torchvision, torchaudio를 설치한다. torch install link : https://pytorch.org/get-started/previous-versions/\n예시)\n# CUDA 10.1 pip install torch==1.8.1+cu101 torchvision==0.9.1+cu101 torchaudio==0.8.1 -f https://download.pytorch.org/whl/torch_stable.html 4. 터미널 또는 Jupyter notebook 스크립트에서 위 설치 명령문을 실행해서 설치한다.\n5. 잘 설치되었는지 확인한다\n6. jupyter notebook을 restart한다 (중요)\n위 내용이 반영되기 위해 재시작한다. 7. GPU사용가능한지 확인한다.\n추가. gpu사용 코드를 돌릴때는 model과 계산하려는 tensor를 모두 gpu device로 할당해야한다.\ndevice = torch.device(\u0026lsquo;cuda\u0026rsquo; if torch.cuda.is_available() else \u0026lsquo;cpu\u0026rsquo;) model.to(device) → 모델을 gpu(또는 cpu)로 tensor데이터.to(device) → 계산할 tensor 데이터를 gpu(또는 cpu)로 추가. 만약, 위 방법으로 CUDA와 torch의 버전을 일치시켰음에도 torch.cuda.is_available()이 False로 출력되는 경우\n이 경우, 실제 사용중인 CUDA의 버전이 \u0026lsquo;watch -n -d nvidia-smi\u0026rsquo;에서 확인되는 버전과 다를 수 있다.\n이때, 실질적인 cuda 사용버전을 확인하는방법은\nls -al /usr/local 경로 내부에서 실제 사용중인 cuda의 버전을 확인한다.\n확인해보면, 실제로는 cuda 11.4 버전이 사용되고 있을 확률이 높다.\nuninstall 진행 후 다시 torch버전을 cuda 11.4에 맞춰서 설치할 경우, 정상적으로 gpu를 사용할 수 있는것을 확인할 수 있다.\n추가. CUDA버전을 확인하는 여러가지 방법\n**- (중요) **\u0026rsquo;nvcc \u0026ndash;version\u0026rsquo; :\n현재 시스템에 설치되어있는 CUDA 컴파일러 버전을 의미한다.\n다만, 단순히 이 정보는 단순히 시스템에 설치된 CUDA 컴파일러의 버전일 뿐, 현재 실행 중인 애플리케이션이나 라이브러리가 실제로 사용하고 있는 CUDA 버전을 반드시 나타내는 것은 아니다.\n- \u0026lsquo;import torch\u0026rsquo;\n- \u0026lsquo;print(torch.version.cuda)\u0026rsquo;\n현재 실행중인 라이브러리(torch)가 호환되는 CUDA버전을 의미한다. 설치된 CUDA버전과는 무관하다.\n","date":"2023.07.21","externalUrl":null,"permalink":"/posts/61/","section":"글","summary":"","title":"[CUDA] torch.cuda.is_available() 를 True로 만들자! (GPU셋팅하기)","type":"posts"},{"content":" # CDW, PMS 연동 # 이번 라벨링 도구 배포의 핵심은 의료 플랫폼내에서 구축한 여러 서비스들을 하나로 동작하게끔 구현하는것인데,\n먼저 데이터가 적재된 CDW와 데이터에 라벨을 마킹하기위한 위한 라벨링도구, 데이터를 분석하기 위한 WIDE를 통합해서 전체 프로세스가 서로 유기적으로 동작하게끔 구현하는것이 목표 였다.\n지금까지는 라벨링 도구내에서 Frontend 파트와 협의의 통한 POC level의 구현이라, 별다른 큰 이슈는 없었고 소통도 수월했지만, CDW, PMS측과 함께 개발을 진행하면서\n조금 더 꼼꼼한 커뮤니케이션이 필요했다.\n→ 아쉬웠던 점\n사실, 전체 개발 볼륨을 지금 생각해보면, 지금 일정보다 조금 더 빠르게 완료할 수 있었던 부분이 있지 않을까 생각한다.\n물론, CDW 및 PMS에서 작업결과를 주고받으며 진행해야했기 때문에 지연된 부분이 있었지만, 분명 내부적으로도 빠르게 풀어낼 수 있는 몇몇 순간들이 있었기때문에, 어떤 부분이 문제였는지 고민해보려한다.\n첫째. CDW 및 PMS에서 전달해준 API들의 용도와 호출 및 응답값의 용도를 파악하고 어떤 순서로 호출해서 원하는 값을 얻을지에 대해 정리하는데 많은 시간이 소요되었다.\n처음부터 전달받은 API들의 목적과 파라미터에 대해서 좀 더 구체적으로 집요하게 물어보고 이해했어야 했는데, 약간의 설명을 듣고 확실히 이해하지 못한 상태에서 쓸데없이 혼자 추리하는데 많은 시간을 허비했다.\n이 부분만 빨리 이해하고 넘어갔어도, 작업 결과를 훨씬 더 빨리 확인할 수 있지 않았을까 한다.\n둘째. CDW 및 PMS쪽에서 제공해준 API를 적용하고 테스트하는데 있어, 물리적인 작업 소요시간이 오래걸렸다.\nPMS로부터 전달받은 dicom내 이미지 URL을 어떠한 방식으로 frontend에 전달할지에 대해, 어떻게 해야 빠르게 화면에 바인딩할 수 있을지에 대해 답이 떠오르지 않았다.\n이미지 단위로 서버내에서 다운로드URL 호출 후 http response형태로 전달하면 해결될 문제였는데, byte로 decoding후 전달하다, byte가 너무 커 timeout되는 문제 등 별 삽질들이 있었고, 시행착오 끝에, 정상적으로 전달할 수 있었다.\nPMS에서 제공하는 데이터의 URL은 외부에서 바인딩이 불가하기때문에, 서버내에서 다운받아 response 할 수 밖에없었지만, 대개의 경우에는 다운로드 URL 아닌, 이미지 자체 URL을 바로 전달할 수 있을것.\n아무쪼록 다음에는, 질문을 부끄러워 말고, 집요하게 내용을 이해하고 문제를 빠르고 정확하게 정의하고 넘어가자.\n# DICOM 개념 추가 # 중간에 프로젝트 메인화면에서 DICOM 개념이 추가되었는데, 이 변경사항은 전체 코드의 core가 흔들리는 부분이었기 때문에, 첫 기획 리뷰 당시에 공수 및 개발 난이도에 대해서 충분히 공감대를 형성했어야 했다.\n첫번째, 기획 변동사항에 대해, 해당 내용의 개발볼륨이 크거나 정책적으로 중요한 변경이라고 판단된다면, cell리더에 선행적인 판단을 요청해야 한다.\n간단한 개발내용이었다면, 크게 문제는 없었겠지만, 주요정책이 바뀌는 부분에 있어, 경험적 판단(좀 더 쉬운방향이 있을지, 실제 개발이 필요한 부분인지\u0026hellip;등)이 필요한 부분이 있기때문에, 리뷰를 받기 전 cell리더에 공유가 된 내용인지 먼저 확인을 해야한다.\n두번째, 리뷰 참석 시, 해당 변경내용에 대한 정확한 개발 볼륨을 예상할 수 있어야 한다.\n이번에 가장 뼈아픈 부분이었는데, 큰 고민없이, 데이터 저장 시, 저장 구조만 바꾸면 되지않을까라는 안일한 생각을 했다.\n실제로 변경된 부분이 core 정책이었기 때문에, state update, dicom 및 개별이미지 저장, 조회, AI라벨링 등 거의 대부분 API의 로직 및 쿼리를 수정해야했다.\n이 부분에 대한 판단을 제대로 못했기 때문에, 작업 예상 공수와 개발 난이도에 대해 기획 및 frontend와 충분한 공감대를 가지지 못했고, 이로 인해 개발 시, 오해와 차질이 있었다.\n확실하게 판단이 되지않는다면, 즉각적인 대답을 피하고 검토 후 답변하면 될 일이다. 급하게 판단하지 말고.\n# 방화벽\u0026hellip; # 보안이 엄격한 의료 도메인 서비스 특성 상 수많은 방화벽 기안을 올려야했다.\n거기다, 공통 플랫폼 및 여러 병원이 각각 격리된 별개의 서버에서 구현되어야 했기에, 상신한 결재가 x N으로 진행되었는데\n배포일정에 임박해서, 열리지 않은 방화벽을 다수 발견했다.\n특히, 앞으로 병원이 추가될때마다 독립된 서버들도 그만큼 추가될 것이기때문에 서비스 별, 서버 별 방화벽 정책 관리페이지를 만들어서 관리를 하도록 하자.\n# 슬슬 복잡해지는 코드와 모듈화의 필요성 # 하나의 프로덕트에서 승연씨와 각자 개발을 진행하고, 이에 대한 소통이 부족하다보니, 동일한 기능에 대해 별도로 개발한 코드들이 많이 늘어났다.\n개발 진행 시, 공통적으로 사용하는 코드, 가령 dicom데이터를 AI라벨링 기능 돌리거나, 직접 라벨링 진행 시,\n개별 이미지의 state update와 dicom 단위의 state update 기능과 같은 중복적으로 사용하는 부분에 대해기능을 모듈화 하고\n공통기능은 반드시 함께 작업하는 인원과 공유하여 중복 개발하는 부분을 줄이도록 하자.\n# 마침 # 초기목표 : 6월 중순까지 의료 라벨링도구 운영기 배포 결과 : 일정 지연(약1~2주가량 지연) 원인 API들의 구조와 사용방법에 대한 이해가 늦은 점 CDW/PMS 와의 커뮤니케이션 방식 미숙 방화벽 정책 관리 소홀 개발 볼륨 예측 실패 모듈화되지않은 코드 의료서버의 보안 정책에 대한 미숙지 개선방안 의료 서버 구축 시, 보안정책(허용된 Header parameter항목 미리 확인) 확인할 것 기획 리뷰 진행 시, 신중한 개발볼륨 및 일정 검토 간단한 이슈 제외하고는 현장에서 확정하지 말 것 팀간의 공유가 된 사항인지 확인 필요 서비스 및 서버별 방화벽 정책 페이지 생성 및 관리 최초 개발 진행 시, 반복적 로직에 대한 모듈화 처리하기 기획 및 frontend와 업무 협의 전 개발 방향에 대한 대략적인 정책 및 부하를 미리 고민한 후 참석할 것 ","date":"2023.07.21","externalUrl":null,"permalink":"/posts/60/","section":"글","summary":"","title":"2023.06월 회고","type":"posts"},{"content":"보통 여러 인원과 함께 프로젝트를 진행할때, 구성원간 유기적이고 신뢰성있게 코드를 관리하는것은 매우 중요하다.\n오늘은 gitlab을 활용해서 손쉽게 코드 및 업데이트 내용을 관리하고, 소스코드의 히스토리를 확인, 공유할 수 있는 협업 방식에 대해\n포스팅해보려 한다.\n먼저, Git과 GitLab에 대해서 간단히 알아보자.\nGit이란? # git 은 소스 코드 버전 관리 시스템으로 로컬에서 변경 사항을 추적하고 원격 리소스에서 변경 사항을 푸시하거나 가져올 수 있다.\n# # GitLab 이란? # GitLab은 깃 저장소 및 CI/CD, 이슈 추적, 보안성 테스트 등의 기능을 갖춘 웹 기반의 데브옵스 플랫폼으로 개인 또는 조직이 Git repository 의 내부 관리를 제공하는데 상용할 수 있는 Github 으로 즉 비공개된 Github라고 할 수 있다.\n실무에서는 7:3정도로 github보다 Gitlab이 많이 사용된다고 한다.\n본론으로 들어가서, 보통 프로젝트를 진행할때 함께 진행하는 구성원과 코드관리에 있어 몇몇 문제들이 발생할때가 있는데,\n커밋하고나서 머지하려고 보니, 충돌나는 경우 서로 같은 로직을 이중개발하는 경우(코드 또는 개발내용에 대한 공유가 잘 이뤄지지 않은 경우) 서로 합의되지않은 코드가 배포되는 경우(문제가 발생할 소지가 있는 코드가 포함되었지만 cross check가 되지않은 경우) 서로의 개발내용(코드)에 대해 이해도가 낮아, 퇴사자 발생 시, 인수인계가 힘들어지는 경우 이와 같이, 소스 관리의 시스템이 갖춰지지 않을 경우 발생할 수 있는 문제는 정말 많다.\n그럼 이제 서로 소스코드를 공유하고, 이력관리 및 업데이트 내용에 대한 구성원들간의 합의를 이룰수있는\n깔끔한 GitLab 활용 방법을 알아보자.\n1) 먼저, 구성원들의 개발 내용들은 모두 별개의 Branch로 관리되어야 한다.\n여러 구성원과 함께 진행되는 프로젝트의 경우 서로간의 소스코드가 충돌하거나 영향을 미칠 수 있다.\n그렇기 때문에, 각자의 개발 내용은 별도의 브랜치로 관리되어야하는데,\n이는, 문제가 발생할 경우, 관련 branch의 수정 내용에 대해서만 확인하면 손쉽게 해결이 가능하고, 문제가 클 경우\n빠르게 이전 환경으로 rollback하는 등 빠른 대처가 가능해지기 때문이다.\n따라서, 각 개발내용을 별도의 브랜치에서 독립적으로 관리하는것이 반드시 필요하다.\n아래는 프로젝트에 참여하는 구성원들이 목적에 맞게 branch를 관리하고,\nGitLab의 merge request를 통해 소스코드 검증체계를 갖추었을때의 업무 프로세스이다.\n유재석 사원이 페이지 조회 기능을 개발했을때 -\u0026gt; \u0026lsquo;feature/search\u0026rsquo; 라는 이름으로 Branch를 만들어 관리한다. 박명수 대리가 파일 삭제 오류를 수정했을때 -\u0026gt; \u0026lsquo;issue/file_delete_error\u0026rsquo; 라는 이름으로 Branch를 만들어 관리한다. 유재석 사원과 박명수 대리는 각각 테스트를 완료한 후 -\u0026gt; \u0026lsquo;dev\u0026rsquo;라는 개발서버로 merge requests한다. 정준하 팀장은 \u0026lsquo;dev\u0026rsquo;에 등록된 merge request 내용을 확인하고, 필요한 부분에 대해 comment를 남김으로써 소스의 문제점을 수정하도록 지시한다. 확인이 완료된 개발서버 소스에 대한 배포를 진행한다. 개발서버에 배포된 내용에 대한 테스트가 완료되면 -\u0026gt; \u0026lsquo;master\u0026rsquo; 라는 운영서버로 merge requests 한다. 마찬가지로, master에 등록된 merge request 내용에 문제가 없을 경우 운영서버 배포를 진행한다. \u0026lt;branch 생성 방법\u0026gt;\n위와 같이 branch명 앞쪽에 작업한 내용에 대해 간단히 기재할 경우, 좀 더 작업물에 대한 빠른 파악에 용이하다.\n작업물 성격(Type)은 아래와 같이 요약되어 사용되는 경우가 많으니 참고하자.\n이제, 위 과정들에 대해서 조금 더 자세히 알아보자.\n먼저, 특정 브랜치\u001d\u001d의 작업이 완료되었고, 해당 내용을 운영서버에 배포한다고 가정하자.\n이때, GitLab에는 merge한 내용을 구성원들에게 공유하고, # 소스코드를 상호 검증할 수 있는 기능(관리방식)이 있는데, 이것을 \u0026lsquo;merge requests\u0026rsquo;라고 한다. # 해당 프로젝트의 책임자 또는 배포 담당자에게 merge request를 등록함으로써, 작업 결과물을 투명하게 공유하고,\n동시에 배포 내용에 대한 상호 검증 기능을 하게 된다.\n예를들어, 해당 Merge Requests건에 대해서 과반 이상의 구성원 동의를 받았을 경우에만 배포를 진행하도록 시스템을 구축한다면,\n업데이트 과정에 강력한 안전장치가 될 수 있을 것이다.\n2) master에서 git을 merge한 후 해당 프로젝트의 GitLab 화면에서 [Merge Requests]를 선택한다.\nMerge Requests를 선택한 화면에서 [open], [Merged], [Closed], [All] Tab을 확인할 수 있고, 클릭할 경우, 상태별 Merge Reqeusts 내용을 확인할 수 있다.\n3) [New Merge requests]를 선택한다.\n4) 그리고, Source Branch와 Target Branch에서 각각, 작업한 소스 브랜치와 배포할 타겟 브랜치를 선택한다.\n5) 마지막으로, 해당 Merge Requests에 대한 담당자와 상세 내용을 기재한다.\n여기서, 모든 작업물에 대해서 별도의 브랜치를 생성할 경우, 매우 많은 브랜치가 생성되고 merge이후에 완료된 브랜치는 추후 삭제를 진행하는 작업을 해야할 수 있는데, 이것에 대한 삭제 옵션 기능을 제공하고있다.\nMerge Requests 화면 하단의 **\u0026ldquo;Remove source branch when merge request is accepted\u0026rdquo;**라는 옵션을 선택할 경우 머지가 확정된 이후 해당 merge requests에 포함된 브랜치는 모두 삭제된다.\n해당 프로젝트가 공유된 구성원들은 해당 Merge requests내용을 확인할 수 있으며,\n아래와 같이 수정 반영된 소스코드에 대해 의견을 남기게 된다.\n만약 구성원 중 과반(제안)이상의 인원이 해당 요청에 대하여 동의(위로 엄지)한다면, 충분히 검증된 내용으로 판단하여\n담당자가 Master merge를 검토할 수가 있다.\n오늘은 GitLab을 이용한 소스코드 관리 및 공유 시스템에 대해서 정리해보았다.\n-\u0026gt; 위 내용을 간단히 정리해보면,\n먼저 GitLab의 Merge request기능을 통해 멤버들은 서로의 코드 내용에 대해 공유할 수 있기때문에, 코드의 충돌을 방지하고, 공통소스를 공유하여 사용할 수 있다.\n그리고, 배포 결정에 있어 신뢰성과 안정성을 가질 수 있기 때문에, 매우 유용한 프로세스\b로 활용될 수 있다.\n","date":"2023.07.21","externalUrl":null,"permalink":"/posts/59/","section":"글","summary":"","title":"[GitLab]소스코드 관리, GitLab으로 완벽 해결하기","type":"posts"},{"content":"","date":"2023.07.21","externalUrl":null,"permalink":"/tags/git/","section":"Tags","summary":"","title":"Git","type":"tags"},{"content":"","date":"2023.07.21","externalUrl":null,"permalink":"/tags/github/","section":"Tags","summary":"","title":"Github","type":"tags"},{"content":"","date":"2023.07.21","externalUrl":null,"permalink":"/tags/gitlab/","section":"Tags","summary":"","title":"Gitlab","type":"tags"},{"content":"","date":"2023.07.21","externalUrl":null,"permalink":"/tags/merge/","section":"Tags","summary":"","title":"Merge","type":"tags"},{"content":"","date":"2023.07.21","externalUrl":null,"permalink":"/tags/%EB%B0%B0%ED%8F%AC/","section":"Tags","summary":"","title":"배포","type":"tags"},{"content":"","date":"2023.07.21","externalUrl":null,"permalink":"/tags/%EB%B0%B0%ED%8F%AC%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4/","section":"Tags","summary":"","title":"배포프로세스","type":"tags"},{"content":"","date":"2023.07.21","externalUrl":null,"permalink":"/tags/%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8/","section":"Tags","summary":"","title":"업데이트","type":"tags"},{"content":" 주제 회고내용 라벨링 도구 [API 개발] 단순 API 개발 진행중에는 큰 문제가 없었다. 그리고, 다른 부서와 API를 연동하면서 여러 변동 상황에 취약해졌고, 각자의 API 및 관련 개발정책의 변경사항이 있을때, 서로간의 공유와 커뮤니케이션이 얼마나 중요한지 깨닫게 되었다. 여러변 변경사항이 발생할때, 너무 휘둘렸다. \u0026gt; 뭐, 이 부분은 관련 TF채널에서 좀 더 소통을 강화할 필요가 있겠고, 구두로 전달하기 보다는 업데이트 된 명세서로 소통할 수 있도록 정리하자. [슬슬 대두되기 시작한 서비스의 속도문제] 첫번째, 우선, 바인딩할 데이터의 물리량이 늘어났다.. 수천, 수만장의 이미지를 로드할때, 물리적으로 시간이 오래걸린다. 이럴때, 비동기로 처리하는것 외에는 방법이 없을까..? \u0026gt; 우선 전체 데이터의 크기를 먼저 전달받고, 바인딩 진행상황을 프로그래스로 표현한뒤 진입 제한하는 방안으로 처리는해야겠다. 다만, 궁극적으로 속도 해결 부분에 있어, 고민이 필요하다. PMS쪽과 개선방안에 대해서 한번 더 논의해보자. 두번째, 이미지 다운로드 URL을 받아서, httpresponse를 진행할때, 속도가 생각보다 느리다. \u0026gt; 이미지 URL형태가 아니라, 바로 띄울 수 있는 형태로 전달할 수 있도록 구조를 바꿔보자. 이 부분도 PMS쪽과 다시 한번 협의해보자. \u0026gt; 그리고 전체 API들에 대해서 속도개선이 필요한 부분을 추려서 개선방안을 강구해보자. [서버간 통신 매커니즘과 innerG/W Architecture에 대해서 정리하자] 이 부분에 이해가 약해서, 이번에 좀 해맨 부분이 있다. 1) WAS와 HA, 로드밸런서 부분에 대한 좀 더 정확한 이해를 위해 관련 내용을 공부하고 포스팅하자 2) innerG/W 구조와 사용방식에 대해서 회사 wiki에 정리하자 3) OAuth, JWT(JSON Web Token) 등의 인증 및 권한 부여 방법 이해에 대해서 좀 더 공부하고 포스팅 하자. 4) 쿠키에 담긴 세션정보를 내가 받는 부분까지의 구체적인 프로세스를 다시 살펴보자. 5) 운영기 WAS와 DB연결 상태 확인 방법에 대해서 다시 살펴보기 6) 운영기에서 쿠키정보가 안넘어온다는것을 확인한 루트에 대해서 살펴보기 당뇨CDSS [첫 회의 잡기가 너무 힘드네] 지금 데이터 EDA를 진행하고, 기초 시각화 자료를 뽑기 위한 통합 데이터 생성에 시간이 너무 많이 걸리고있다. 우선, 기획분은 섣불리 가져갔다가, 그대로 확정될 수 있고, 팀내 단독으로 기획분을 제시하는것이 무리가 있으므로, 우선 통합 로직의 합리성에 대한 검증과 관련 시각화 내용을 보여드리고, 데이터 처리 방식에 대한 확신을 얻는것이 중요할 것 같다. \u0026gt; 우선 데이터 통합로직에 대한 내부 합의와 시각화를 완료하고 빨리 회의를 잡자 개인공부 [알고리즘 공부는 계속 꾸준히 하자구] 확실히, 알고리즘을 계속 공부하는게 업무에 바로바로 적용되면서 코드 개선에 큰도움이 되고있다. 비슷한 형태의 코드만 돌려쓰는것이 아니라, 계속 새로운 방향으로 고민해보고 적용해서 개선하게끔 생각하게 되는 장점이 있다. 그리고, 처음부터 클린하고 효율적인 형태의 코드를 짜기 위해서도 알고리즘은 계속 난이도를 높여서 공부해나가는게 좋겠다. [모델개발에 대해서 좀 더 개인 과제를 만들어서 진행하는것이 필요할듯 싶다] 민지씨와 얘기했던 것 처럼, 서비스 내부에서 AI를 통해 개선을 시도해볼만한 컨텐츠 발굴을 계속해야겠다. 그리고 3개월 내 결과물을 확인할 수 있을 정도의 분량 및 난이도면 좋겠다. 젠데스크 개선방안에 대해서 민지씨와 얘기해보고 프로젝트를 진행해보도록 하자. 정리 아마, 다음달에는 부서개편이 있을 예정이고, AI모델의 실서비스 적용과 개발에 업무 초점이 강화될 예정이다. 다음달은 슈퍼북 고도화 모델개발, 당뇨CDSS관련 시각화 마무리 및 회의 진행, 라벨링도구 개선 세가지를 조금 촘촘한 일정으로 진행하고, 이에 대한 상세계획을 세우자. 그리고, 알고리즘과 개인 Task도 진행할 수 있도록 할 것. ","date":"2023.07.11","externalUrl":null,"permalink":"/posts/58/","section":"글","summary":"","title":"2023.05월 회고","type":"posts"},{"content":"최근, 머신러닝이 활용되는 분야 중, 어떻게 보면 가장 많은 빈도로 활용되고있는 추천 시스템에 대해서 정리해보려 한다.\n추천 시스템이란?\n간단하게 말하자면, 사용자의 취향이나 필요성을 파악해서 그에 맞는 아이템을 추천해주는 시스템이다. 넷플릭스에서 볼 영화 추천받거나, 쇼핑몰에서 살 물건 추천받는 거라고 생각하면 이해하기 쉬울듯하다.\n검색과는 약간의 차이가 있는데, 검색이 사용자가 직접 구하고자 하는 내용을 찾는 행위라면, 추천시스템은 사용자가 찾기전에 가장 유용할 것으로 예상되는 정보를 먼저 제시하는 개념이다.\n이런 추천 시스템은 수 많은 방법론을 가지고 있는데, 그 중 가장 대표적으로 두 가지 방법론으로 나눌 수 있다.\n바로 **\u0026lsquo;컨텐츠 기반 추천\u0026rsquo;과 \u0026lsquo;협업 필터링\u0026rsquo;**이라는 방법이다.\n추천시스템 방법론의 종류\n가장 대표적인 고전 방법론인 \u0026ldquo;컨텐츠 기반\u0026quot;추천과 \u0026ldquo;협업 필터링\u0026quot;을 비교해보자. # 구분 컨텐츠 기반(content-based) 협업 필터링(Collaborative Filtering) Concept \u0026ldquo;스파이더맨에 4.5점을 부여한 유저는 타이타닉보다 캡틴 마블을 더 좋아할 것이다.\u0026rdquo; → 사용자가 과거에 좋아했던 아이템을 파악하고 그 아이템과 비슷한 아이템을 추천 \u0026ldquo;유저 A와 B가 아이템 \u0026lsquo;I\u0026rsquo;에 대해 비슷한 평가를 내렸다면, 유저 A가 선호하는 다른 아이템인 \u0026lsquo;J\u0026rsquo;에 대해서도 유저 B가 비슷한 선호도를 가지고 있지 않을까?\u0026rdquo; 세분화 neighborhood method (예.KNN) 아이템 사이 또는 유저사이의 관계를 계산하는것에 초점을 맞추는 방법론으로 동일한 아이템에 유사한 평가를 내린 유저들은 이웃이 됨 Latent factor model (예.Matrix Factorization) user의 item평가에서 추출된 20~100개의 latent factor을 활용하여 유저와 아이템을 함께 특성화(feature extraction)하는 방법 장점 1) 다른 유저의 데이터가 필요하지 않다. 2) 추천할 수 있는 아이템의 범위가 넓다 \u0026ndash;\u0026gt; Unique, New, Unpopular 아이템 모두 추천 가능 3) 추천하는 이유를 제시할 수 있다. \u0026ndash;\u0026gt; 아이템의 Feature로 컨텐츠 분석 가능 \u0026ndash;\u0026gt; 특정 feature가 추천의 이유가 됐다고 설명가능 \u0026mdash;\u0026gt; 해당 유저가 좋아했던 아이템을 관찰하면, 추천결과를 이해할 수 있음 단점 1) 적절한 features를 찾기가 어렵다 \u0026ndash;\u0026gt; 영화, 사진, 음악, 뉴스(텍스트) 등 수많은 Feature들에서 적절한 조합을 찾는것이 어렵다. 2) 새로운 유저를 위한 추천이 어렵다. \u0026ndash;\u0026gt; User Profile이 존재하지 않거나 데이터가 매우 부족하다. 3) 선호하는 특성을 가진 항목을 반복 추천한다. \u0026ndash;\u0026gt; Overspecialization \u0026ndash;\u0026gt; 유저의 다양한 취향 반영이 어렵다. \u0026ndash;\u0026gt; 유저 프로필외 추천 불가 Architecture 활용예시 Pandora(음악사이트) - 신곡 출시 → 음악을 분석하여 장르, 비트, 음색 등 400여 항목 체크 → Features Extraction - 유저로부터 \u0026lsquo;Like\u0026rsquo;를 받은 음악의 특생과 해당 유저의 profile 준비 → Profile learner - 음악의 특징과 사용자 프로필을 바탕으로 선호하는 음악 추천 → Filtering componen 중요한 점 - 컨텐츠의 내용을 분석하는 아이템 분석 알고리즘 - Clustering, Machine Learning, TF-IDF 등 활용 - 추천 시스템의 성능을 높일 수 있는 적절한 컨텐츠(feature)사용 - 영화추천을 위해 효과적인 영화정보 활용 첫 번째 \u0026lsquo;컨텐츠 기반 추천\u0026rsquo;\n이 방법은 사용자가 과거에 좋아했던 아이템의 내용을 분석해서, 그와 비슷한 아이템을 추천해주는 방식이다.\n예를 들어, \u0026lsquo;인셉션\u0026rsquo;이라는 영화를 좋아했다면, 이 영화의 특징(감독, 배우, 장르 등)을 분석해서 비슷한 특징을 가진 다른 영화를 추천해주는 방법이다.\n이 방법의 장점은 새로운 사용자가 왔을 때도, 그 사용자의 특정 아이템에 대한 선호도만 알면 추천을 해줄 수 있다는 점이다.\n하지만 단점도 있는데, 사용자가 과거에 좋아했던 아이템과 비슷한 아이템만 추천해주기 때문에, 새로운 취향을 발견하기 어렵다.\n두번째, \u0026lsquo;협업 필터링\u0026rsquo;\n이 방법은 사용자와 아이템 간의 상호작용을 기반으로 추천을 해주는 방식이\u001d다.\n\u0026lsquo;**사용자 기반 협업 필터링\u0026rsquo;**과 \u0026lsquo;아이템 기반 협업 필터링\u0026rsquo; 두 가지 방법이 있는데, 사용자 기반 방법은 사용자 간의 유사도를 계산해서, 비슷한 취향을 가진 다른 사용자가 좋아하는 아이템을 추천해주는 방식이고, 아이템 기반 방법은 아이템 간의 유사도를 계산해서, 사용자가 좋아하는 아이템과 비슷한 다른 아이템을 추천해주는 방식이다.\n예를 들어, 당신과 비슷한 영화 취향을 가진 사람이 \u0026lsquo;인터스텔라\u0026rsquo;를 좋아했다면, \u0026lsquo;인터스텔라\u0026rsquo;를 당신에게 추천해주는 방식이다.\n이 방법의 장점은 사용자의 새로운 취향을 발견할 수 있다는 점이다. 과거에 좋아하지 않았던 아이템이라도, 비슷한 취향을 가진 다른 사용자가 좋아했다면 그 아이템을 추천해줄 수 있기때문이다.\n하지만 이 방법도 단점이 있는데, 새로운 사용자나 새로운 아이템에 대한 추천이 어렵\b다는 점이다. 이를 \u0026lsquo;콜드 스타트 문제\u0026rsquo;라고 부르는데, 사용자나 아이템에 대한 정보가 충분하지 않을 때 추천의 정확도가 떨어지는 문제를 말한다.\n","date":"2023.06.26","externalUrl":null,"permalink":"/posts/56/","section":"글","summary":"","title":"컨텐츠 기반 추천 vs 협업 필터링","type":"posts"},{"content":"서버 접속 시, 3개월에 한번씩, 비밀번호를 변경하라는 번거로운 안내가 출력된다.\n다만, 이 비밀번호 생성 규칙이 너무 번거로운 탓에 겨우 규칙에 맞춰 한번 설정하고나면,\n까먹고 비밀번호를 기억하지못할때가 많다.\n그래서, 3개월 주기로 출력되는 비밀번호 변경안내에 따라 변경 한 후\n로그인된 상태에서 비밀번호를 변경하면,\n별도의 규칙(물론 보안에 대한 경고는 노출되지만 사뿐히 무시하고)없이\n원래 사용 하던 비밀번호로 변경할 수 있다.\n아래 과정을 확인해보자!\n1. 일단 규칙에 맞춰서 기억하지못할 비밀번호로 임시설정을 완료한다.(그냥 Qwer1234!@#$)\n2. 변경된 비밀번호로 로그인!\n3. \u0026ldquo;passwd\u0026quot;를 통해 다시 비밀번호를 변경하자\n4. 현재 비밀번호를 입력한다.(Qwer1234!@#$)\n5. 새로운 비밀번호를 입력한다. ( 난 원래 쓰던걸로 다시\u0026hellip;ㅎㅎ)\n6. 이전과 비슷한 패턴이라는 보안관련 경고가 출력되지만 사뿐히 무시하고 새 비밀번호를 다시 입력해주면 끝!!\n","date":"2023.05.08","externalUrl":null,"permalink":"/posts/52/","section":"글","summary":"","title":"[Linux] 서버 접속 비밀번호 변경","type":"posts"},{"content":"","date":"2023.05.08","externalUrl":null,"permalink":"/tags/%EB%B3%80%EA%B2%BD/","section":"Tags","summary":"","title":"변경","type":"tags"},{"content":"","date":"2023.05.08","externalUrl":null,"permalink":"/tags/%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8/","section":"Tags","summary":"","title":"비밀번호","type":"tags"},{"content":" 1. app root directory 내부에 가상환경(venv)이 생성되어있는지 확인 # 2. 현재 가상환경 연결이 잘 되어있는지 확인 (source venv/bin/activate) # 3. Preferences setting 확인 # Languages \u0026amp; Frameworks \u0026gt; Django Django project root 에 root directory설정이 잘되어있는지 확인 settings에 settings.py 경로가 잘 셋팅 되어 있는지 확인 Project: [서비스폴더(root folder명)] python interpreter : 파이썬 인터프리터 설정 제대로 되어있는지 확인(가상환경 내 python 경로) 4. Configuration # Environment \u0026gt; Environment variables DJANGO_SETTINGS_MODULE=app.settings 확인 Python Interpreter : 파이썬 interpreter경로 확인 참고(번외) PYTHONUNBEFFERED=1 \u0026lt;\u0026ndash; 이거 뭐? 위 변수는 출력 버퍼링을 셋팅하는 변수이다. 출력 버퍼링이란, 프로그램의 출력 데이터를 일시적으로 저장하는 메모리 영역인 버퍼에 저장한 후, 일정량이 쌓이면 한 번에 출력하는 기법을 말하는데, 출력 버퍼링의 목적은 주로 성능 향상과 입출력(I/O) 효율을 높이기 위해 설정한다. 출력버퍼링 설정 이유 성능 향상: 출력을 버퍼링하면 여러 번의 작은 출력 대신 한 번에 큰 덩어리로 출력할 수 있기 때문에, 시스템 자원을 덜 사용하고 출력 작업이 더 빨라\b진다고 한다. 입출력 효율: 출력 장치(예: 디스크, 콘솔, 네트워크)와의 통신 비용을 줄일 수 있다. 출력 작업을 매번 수행하지 않고, 데이터가 충분히 쌓이면 한 번에 전송하게 한다. 정렬 및 조합: 출력 데이터를 버퍼에 저장하면, 출력을 정렬하거나 다른 데이터와 조합할 수 있는 기회가 생긴다. 이를 통해 출력 결과를 더 읽기 쉽게 만들거나, 필요한 경우 데이터를 변경할 수 있다. 출력버퍼링 설정 단점 출력 버퍼링을 사용하면 당연히, 디버깅이 어려워질 수 있다. 버퍼링된 출력은 실시간으로 표시되지 않으므로, 오류가 발생했을 때 즉시 확인할 수 없고 이 때문에, 디버깅 중이거나 실시간 로깅이 필요한 경우 출력 버퍼링을 비활성화해야 한다.(1로 설정) 출력 버퍼링을 비활성화하면 파이썬은 버퍼를 사용하지 않고 출력을 즉시 표시한다. 이렇게 하면 디버깅 및 로깅을 더 쉽게 수행할 수 있지만, 애플리케이션의 성능에 약간의 영향을 미칠 수 있다\u001d. 5. 이상태에서 runserver 진행 시, 자꾸 not module error가 뜬다? # 폴더의 목적에 맞게 지정가능\n혹시, project root 폴더 및 하위 소스 폴더 아이콘이 비활성화(회색)상태인가? 이 경우, 루트폴더를 소스 폴더로 인식하지 못해, settings.py 또한 읽어들어가지 못하기때문에 not found error가 발생하는것으로 보인다. 우클릭, Mark Derectory as \u0026gt; Sources Root 선택해서 소스 루트로 설정해준다 파랑색아이콘으로 변경되었다. 이제 이 폴더를 소스폴더로 인식하다는 뜻이다. 다시 runserver 진행 -\u0026gt; 정상 ","date":"2023.04.06","externalUrl":null,"permalink":"/posts/50/","section":"글","summary":"","title":"[파이참] django를 활용한 App setting시 자잘한 체크리스트","type":"posts"},{"content":"","date":"2023.04.06","externalUrl":null,"permalink":"/tags/django/","section":"Tags","summary":"","title":"Django","type":"tags"},{"content":"","date":"2023.04.06","externalUrl":null,"permalink":"/tags/pycharm/","section":"Tags","summary":"","title":"Pycharm","type":"tags"},{"content":"","date":"2023.04.06","externalUrl":null,"permalink":"/tags/venv/","section":"Tags","summary":"","title":"Venv","type":"tags"},{"content":"","date":"2023.04.06","externalUrl":null,"permalink":"/tags/%EA%B0%80%EC%83%81%ED%99%98%EA%B2%BD/","section":"Tags","summary":"","title":"가상환경","type":"tags"},{"content":"","date":"2023.04.06","externalUrl":null,"permalink":"/tags/%ED%8C%8C%EC%9D%B4%EC%8D%AC%EC%9D%B8%ED%84%B0%ED%94%84%EB%A6%AC%ED%84%B0/","section":"Tags","summary":"","title":"파이썬인터프리터","type":"tags"},{"content":"","date":"2023.04.06","externalUrl":null,"permalink":"/tags/%ED%8C%8C%EC%9D%B4%EC%B0%B8/","section":"Tags","summary":"","title":"파이참","type":"tags"},{"content":"\bData Scientist에게 가장 중요한 역량중 하나는 최신 알고리즘 및 방법론에 대한 논문을 빠르게 이해하고,\n그 중 양질의 논문을 이해하고, 내가 진행하는 Task에 적절히 녹여낼 수 있는 능력일 것이다.\n\b그래서 오늘은 AI분야에서는 매일 쏟아지는 새로운 논문들 중 양질의 논문을 찾는 방법에 대해 정리해보려한다.\nConference paper? Journal paper?\n논문을 찾다보면, 컨퍼런스 or 저널 이라는 워딩을 많이 볼 수 있다.\n먼저, conference와 journal은 논문이 발행된 곳을 의미한다.\n그렇다면, conference에서 발행된 paper와 journal에서 발행된 paper은 어떻게 다를까?\n1) Conference paper\nconference는 학계, 연구자, 전문가들이 철저한 연구를 거친 연구를 서로 공유하고 발표하는 자리로, 서면 문서와 구두 발표를 포괄하는 개념이다. conference에서 발행된 논문은 학자들이 연구한 내용을 제한된 수의 페이지로 정리하여 발표한 간략하고 정확한 문서를 의미한다.\n2) Journal paper\njournal 논문은 저널에 출판하기 위한 목적으로 작성된 문서로, Conference paper와 비교하여 훨씬 더 철저하고 엄격한 검토를 거치는 내용으로 분량도 Conference paper에 비해 훨씬 많다.\n정리하면, conference paper는 학회에서 연구결과를 간단히 공유하고 발표하기 위한 간략한 문서이고, journal paper는 Journal에 출판하기 위한 목적으로 훨씬 까다로운 심사와 상세한 연구내용을 담고있는 문서로 정리할 수 있다.\n머신러닝 분야를 연구하다보면 Conference paper와 Journal paper을 모두 참고해야하는데,\n아무래도 Conference paper가 학회 발표목적의 간략한 문서로 만들어지다보니, 좀 더 최신의 연구성과들을 담고있어,\nConference paper를 읽을 기회가 비교적 많을 것이다.\n그리고 우리는 수많은 Conference paper중에도 양질의 paper를 선별해서 보고 싶은데, 모든 논문을 하나하나 읽어가며, 양질의 논문을\n걸러내기는 어렵다.\n좋은 논문을 찾기 위한 방법은 크게 두가지가 있다 # 첫번째, 좋은 컨퍼런스(또는 저널)를 통해 찾는 방법이다. # 우리가 좋은 논문을 걸러내기 위해서는 논문을 거를 Filter가 필요한데, Conference가 이 Filter 역할을 할 수 있다.\n대개 좋은 Conference는 paper를 게제하기 위한 심사기준이 더 까다롭다.\n그렇기 때문에, 아무래도 좋은 Conference에 게제된 논문은 좀 더 까다로운 심사를 충족한, 양질의 논문으로 가정할 수 있을 것이다.\n그렇다면, 좋은 Conference를 어떻게 판단할 수 있을까?\nGoogle Scholar에서 AI분야의 Top Conference Ranking을 제공하고 있긴 하지만, AI 전체 카테고리에서 순위를 산정했기 때문에, 각 task별로 좋은 conference를 찾기에는 무리가 있어 보인다.\n* Google Scholar Conference Ranking : https://scholar.google.es/citations?view_op=top_venues\u0026hl=en\u0026vq=eng_artificialintelligence\n[Artificial Intelligence - Google Scholar Metrics\n5.IEEE Transactions On Systems, Man And Cybernetics Part B, Cybernetics142186\nscholar.google.es](https://scholar.google.es/citations?view_op=top_venues\u0026hl=en\u0026vq=eng_artificialintelligence)\n* Research.com : https://research.com/conference-rankings/computer-science/machine-learning\n[Best Computer Science Conferences Ranking Machine Learning \u0026amp; Artificial intelligence 2022 | Research.com\nFind and compare 2400+ international, national and regional conferences on Computer Science and 24 other science disciplines.\nresearch.com](https://research.com/conference-rankings/computer-science/machine-learning)\n리서치 닷컴이라는 사이트도 있지만, Machine Learning 전체 범위에 대한 Top conference를 산정하기 때문에 마찬가지이다.\n계속, 검색해보던 중 각 Task별로 우수 학회를 조회할 수 있는 사이트를 발견할 수 있었다.\n* Conference Ranks : http://www.conferenceranks.com/#\n\u0026lsquo;Rank\u0026rsquo;가 어떤 기준으로 선정되었는지는 알 수 없으나, 우선 위 사이트를 참고하여 AI 분야의 가장 대표적 주제인\nComputer vision과 NLP분야의 우수 학회를 서칭해보았다.\nComputer Vision 우수 학회\nWACV (IEEE Workshop on Applications of Computer Vision)\nICCV (IEEE International Conference on Computer Vision)\nECCV (European Conference on Computer Vision)\nCVPR (IEEE Conference on Computer Vision and Pattern Recognition)\nNIPS (GAN이 처음 발표되었던 학회) \u0026lt;- 요건 별도로 찾은 것\nNLP 우수 학회\nEMNLP (Empirical Methods in Natural Language Processing)\nIJCNLP (International Joint Conference on Natural Language Processing)\nRANLP (Recent Advances in Natural Language Processing)\nNLPCS (International Workshop on Natural Language Processing and Cognitive Science)\n그 중 NLP분야에서 가장 핫한 학회인 EMNLP를 검색해 직접 들어가 보았다!\n이 중 NLP분야의 우수학회인 EMNLP를 검색해보았다.\n검색한 사이트에 들어오면 해당 학회에서 발행된 수많은 논문들이 주제별로 출력되는것을 확인할 수 있다.\n이렇게, Conference를 통해 직접 가장 최신의 논문들을 훑어볼 수 있다.\n다만, conference를 통해 논문을 찾게되면 가장 빠르게 새로 발행된 논문을 볼 수 있다는 장점이 있지만, 그럼에도 불구하고 너무나도 많은 논문들 사이에서 옥석을 가려내는것이 쉬운일이 아니다.\n꼭, 가장 최신이 아니더라도 사람들이 많이 인용하고, 핵심이 되는 논문을 살펴보려 한다면,\n두번째 방법, 주제별로 핫하고 유명한 논문만 모아놓은 사이트에서 논문들을 확인할 수 있다. # https://paperswithcode.com/methods\n[Papers with Code - The Methods Corpus\n2061 methods • 92256 papers with code.\npaperswithcode.com](https://paperswithcode.com/methods)\n위 사이트에서는\n1) ML분야의 카테고리별로 분류\n2) 카테고리 내 상세 주제로 분류(생성, 분류, 회귀 \u0026hellip;)\n3) 상세 주제에서 가장 많은 논문이 발행된 순서대로 알고리즘(방법론)을 소개하고 그에 대한 논문과 오픈소스를 함께 정리하여 공유하고 있다.\n사실, Conference에서 발행된 논문들을 즉시 확인하는 정도의 수준이 아니라면, 대부분의 최신 논문과 오픈소스를 확인할 수 있어,\n해당 사이트에서 충분히 양질의 논문을 찾아볼 수 있지 않나 싶다.\n좋은 논문을 찾기 위한 방법을 알아보려 시작했지만, 결국 같은 고민을 하는 사람들이 잘 정리된 사이트를 만들어 두었기 때문에,\n앞으로는 고민할 필요없이 위 사이트를 이용해서 논문을 접하면 될 것 같다.\n","date":"2023.04.05","externalUrl":null,"permalink":"/posts/49/","section":"글","summary":"","title":"[AI/ML] 좋은 논문 찾는 법!","type":"posts"},{"content":"","date":"2023.04.05","externalUrl":null,"permalink":"/tags/ai/","section":"Tags","summary":"","title":"Ai","type":"tags"},{"content":"","date":"2023.04.05","externalUrl":null,"permalink":"/tags/conference/","section":"Tags","summary":"","title":"Conference","type":"tags"},{"content":"","date":"2023.04.05","externalUrl":null,"permalink":"/tags/journal/","section":"Tags","summary":"","title":"Journal","type":"tags"},{"content":"","date":"2023.04.05","externalUrl":null,"permalink":"/tags/%EB%85%BC%EB%AC%B8%EC%B0%BE%EA%B8%B0/","section":"Tags","summary":"","title":"논문찾기","type":"tags"},{"content":"","date":"2023.04.05","externalUrl":null,"permalink":"/tags/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D/","section":"Tags","summary":"","title":"머신러닝","type":"tags"},{"content":"","date":"2023.04.05","externalUrl":null,"permalink":"/tags/%EC%A2%8B%EC%9D%80%EB%85%BC%EB%AC%B8/","section":"Tags","summary":"","title":"좋은논문","type":"tags"},{"content":" 로컬의 파일을 서버에 올리려고 했는데,\n로컬서버에 파일을 올리려고 했는데, 권한관련 에러가 난다.\n원인은 파일을 옮기려는 서버 디렉토리에 대한 권한이 \u0026lsquo;없어서\u0026rsquo;이다.\n그래서 오늘은 리눅스에서 파일 또는 디렉토리의 사용권한을 수정하는 방법에 대해서 간단히 정리해보려한다.\n1) 현재 유저의 권한 확인해보기\n먼저 파일을 이동하려는 서버 및 디렉토리에 접속한 뒤 파일 또는 디렉토리의 권한상태를 확인할 수 있다.\nls -l # or ls -al # [output] drwxr-xr-x 2 user group 4096 Mar 29 12:34 example_directory output으로 출력되는 내용을 보고 파일 또는 디렉토리의 권한을 확인할 수 있는데, 아래와 같이 이해하면된다.\nd:\n디렉토리임을 나타낸다. 파일의 경우 -로 표시됩니다.\nrwxr-xr-x:\n권한 정보를 나타낸다. 여기서 r은 읽기, w는 쓰기, x는 실행 권한을 나타내며,\n각 문자는 순서대로 소유자, 그룹, 기타 사용자에 대한 권한을 나타낸다.\n권한이 없는 경우 -로 표시\n2:\n해당 파일 또는 디렉토리의 하드 링크 수입니다.\nuser:\n소유자의 이름입니다.\ngroup:\n그룹의 이름입니다.\n4096:\n파일 또는 디렉토리의 크기(바이트)입니다.\nMar 29 12:34:\n최근 수정된 시간입니다.\nexample_directory:\n파일 또는 디렉토리의 이름입니다.\n2) 현재 파일 또는 디렉토리의 소유자 변경하기\n현재 파일 또는 디렉토리의 소유자를 변경하려면 어떻게 해야할까?\nsudo chown username /home/data 소유자와 그룹을 동시에 변경하려면 다음과 같이 입력한다. sudo chown newuser:newgroup file.txt 디렉토리의 경우에는 \u0026lsquo;-R\u0026rsquo; 옵션을 사용하여 재귀적으로 소유자와 그룹을 변경할 수 있다. sudo chown -R newuser:newgroup /path/to/directory 3) 현재 파일 또는 디렉토리의 권한 변경하기\n리눅스에서 파일 또는 디렉토리의 권한은 chmod를 통해 수정할 수 있다\n아래는 file.txt파일의 권한을 수정하는 명령문의 예시이다.\n# 권한변경 sudo chmod 700 file.txt # 디렉토리의 경우 \u0026#39;-R\u0026#39;옵션을 사용하여 재귀적으로 권한을 변경할 수 있다. sudo chmod -R 700 /path/to/directory 파일 또는 디렉토리에 권한을 설정하는 주체가 있는데,\n**첫번째, 소유자(u)**는 파일 또는 디렉토리의 소유자를 의미한다.\n**두번째, 그룹(g)**은 파일 또는ㅈ 디렉토리의 그룹에 속한 사용자를 의미한다.\n**세번째, 기타(o)**는 소유자와 그룹 이외의 사용자를 의미한다.\n그리고, 각 주체별로 부여할 수 있는 권한은\n\u0026lsquo;읽기(r)\u0026rsquo;, \u0026lsquo;쓰기(\u0026lsquo;w)\u0026rsquo;, \u0026lsquo;실행(x)\u0026rsquo; 세가지 권한이 있는데,\n위의 chmod 명령어 사용 시 숫자 또는 문자로 권한을 설정할 수 있다.\n숫자로 권한을 설정하는 경우, 각 권한에 대응하는 숫자를 사용하여 설정하게 된다.\n읽기(r) : 4 쓰기(w) : 2 실행(x) : 1 예를들어 \u0026lsquo;chmod 700\u0026rsquo;은 다음과 같이 권한을 설정한다는 의미이다.\n소유자 : 읽기, 쓰기, 실행 권한(4+2+1 = 7) 그룹 : 권한없음(0) 기타 : 권한없음(0) ","date":"2023.04.03","externalUrl":null,"permalink":"/posts/47/","section":"글","summary":"","title":"[Linux]파일 또는 디렉토리 권한/소유자 수정하기(chown, chmod)","type":"posts"},{"content":"","date":"2023.04.03","externalUrl":null,"permalink":"/tags/chmod/","section":"Tags","summary":"","title":"Chmod","type":"tags"},{"content":"","date":"2023.04.03","externalUrl":null,"permalink":"/tags/chown/","section":"Tags","summary":"","title":"Chown","type":"tags"},{"content":"","date":"2023.04.03","externalUrl":null,"permalink":"/tags/%EA%B6%8C%ED%95%9C%EC%84%A4%EC%A0%95/","section":"Tags","summary":"","title":"권한설정","type":"tags"},{"content":"","date":"2023.04.03","externalUrl":null,"permalink":"/tags/%EB%94%94%EB%A0%89%ED%86%A0%EB%A6%AC%EA%B6%8C%ED%95%9C/","section":"Tags","summary":"","title":"디렉토리권한","type":"tags"},{"content":"","date":"2023.04.03","externalUrl":null,"permalink":"/tags/%ED%8C%8C%EC%9D%BC%EA%B6%8C%ED%95%9C/","section":"Tags","summary":"","title":"파일권한","type":"tags"},{"content":"Flower는 \u0026lsquo;Celery\u0026rsquo;의 웹 모니터링 도구로, 작업 큐를 추적하고 관리하는 도움이 되는 Tool이다.\n최근에는 celery 사용 시 필수적으로 사용되는 도구로, 오늘은 Flower Dashboard의 각 항목을 분해해보려한다.\nFlower 대시보드 화면\n\u0026lsquo;Flower\u0026rsquo; Dashboard\nFlower의 dashboard에서는 위와 같이 각 worker들의 상태와 작업량, 작업부하등의 정보를 보기쉽게 담고있다.\n대시보드의 각 컬럼들이 의미하는바에 대해 정리해보자.\n(1) \u0026lsquo;Worker Name\u0026rsquo;\n워커의 고유한 이름으로 이 이름은 각 워커를 식별하는데 사용된다.\n각 worker name은 celery가 실행되어있는 docker container id를 표시하고 있다.\n(2) \u0026lsquo;Status\u0026rsquo;\nworker의 현재 상태를 표현하는것으로 일반적으로 \u0026lsquo;Online\u0026rsquo; 또는 \u0026lsquo;Offline\u0026rsquo;으로 표시된다.\nOnline은 워커가 정상적으로 작동중임을 의미하며, Offline은 워커가 작동하지 않거나, 연결이 끊겼음을 나타낸다.\n(3) \u0026lsquo;Active\u0026rsquo;\n현재 실행중인 작업의 수를 나타낸다.\n워커가 처리중인 작업의 수가 몇 개인지를 나타낸다.\n(4) \u0026lsquo;Processed\u0026rsquo;\n워커가 시작된 이후 처리한 작업의 총 개수를 나타낸다.\n작업이 성공적으로 완료되거나, 실패했든간에 처리된 작업이 포함된다.\n(5) \u0026lsquo;Failed\u0026rsquo;\n워커가 처리하는 동안 실패한 작업의 개수이다.\n실패한 작업은 예외가 발생하거나 처리중에 문제가 발생한 작업을 의미한다.\n(6) \u0026lsquo;Succeeded\u0026rsquo;\n워커가 처리하는 동안 성공적으로 완료한 작업의 개수이다.\n성공적으로 처리된 작업은 워커가 정상적으로 작업을 완료하고 결과를 반환한것을 의미한다.\n(7) \u0026lsquo;Retried\u0026rsquo;\n워커가 처리하는 동안 재시도된 작업의 개수이다.\n재시도된 작업은 일시적인 문제로 인해 처음에 실패했으나, 설정된 재시도 정책에 따라 다시 시도된 작업을 의미한다.\n* 잠깐, 재시도 정책은 어떻게 설정할까?\ncelery에서 재시도 설정은 작업(task)데코레이터 내에 설정할 수 있다.\n\u0026lsquo;autoretry_for\u0026rsquo; 및 \u0026lsquo;retry_kwargs\u0026rsquo;매개변수를 사용해서 예외유형 및 재시도에 대한 설정을 지정할 수 있다.\n예를들어, 작업이 \u0026lsquo;SomeException\u0026rsquo;예외를 발생시킬 때 재시도하려면 다음과 같이 작업을 정의하면 된다.\nfrom celery import Celery from some_module import SomeException app = Celery(\u0026#39;task\u0026#39;, broker=\u0026#39;pyamqp://guest@localhost//\u0026#39;) @app.task(autoretry_for=(SomeException,), retry_kwargs={\u0026#39;max_retries\u0026#39;:3, \u0026#39;countdown\u0026#39; : 5}) def some_task(): #your task logic here pass 위 예제에서, autoretry_for에 SomeException이 포함되어 있으므로, 해당 작업이 실행 중 SomeException 예외를 발생시키면 자동으로 재시도합니다\n위 예제에서, \u0026lsquo;autoretry_for\u0026rsquo;에 \u0026lsquo;SomeException\u0026rsquo;이 포함되어 있으므로, 해당 작업이 실행 중 \u0026lsquo;SomeException\u0026rsquo;예외를 발생시키면 자동으로 재시도한다.\n\u0026lsquo;retry_kwargs\u0026rsquo; 매개변수는 재시도 관련 설정을 제공합니다.\n\u0026lsquo;max_retries\u0026rsquo;는 최대 재시도 횟수를 지정하고, \u0026lsquo;countdown\u0026rsquo;은 재시도 간 시간 간격(초)을\u001c지정합니다.\n(8) \u0026lsquo;Load average\u0026rsquo;\n워커 시스템의 평균 부하를 나타낸다.\n이 값은 일반적으로 1분 / 5분 / 15분 동안의 시스템 부하 평균을 표시한다.\n시스템 부하는 시스템에서 실행중인 프로세스와 대기중인 프로세스의 총 개수를 나타내며, 시스템 가용 리소스와 관련된 성능지표를 의미한다.\n대시보드에 표현된 정보를 통해 아래와 같이 조치할 수 있다.\n1. 만약, Active값이 높다면\n워커의 부하가 높아질 수 있으므로, 추가 워커를 투입하거나 작업큐의 크기를 조절하여 부하를 줄일 수 있다.\n또한 실패한 작업이 많은 경우 워커 코드를 검토하여 문제를 해결하거나, 시스템 자원을 증가시켜 워커의 처리능력을 향상시킬 수 있다.\n2. Load average가 어느정도 되면 높다고 판단하는걸까?\nload average의 정상범위는 CPU 코어수와 관련이 있는데, 일반적으로 부하평균이 CPU코어 수보다 낮으면 시스템이 정상적으로 작동하고 있는것으로 간주한다.\n예를들어, \u0026ldquo;4개의 CPU 코어가 있는 시스템에서 부하평균(load average)이 4 미만이라면 시스템이 정상적으로 운영되고 있다\u0026quot;고 볼 수 있다\n* CPU 코어 수 확인방법\nnproc nproc 명령어를 통해 cpu코어가 4개인것을 확인\n","date":"2023.03.29","externalUrl":null,"permalink":"/posts/45/","section":"글","summary":"","title":"[Flower]Celery웹 모니터링 도구 'Flower'을 분해해보자.","type":"posts"},{"content":"","date":"2023.03.29","externalUrl":null,"permalink":"/tags/celery/","section":"Tags","summary":"","title":"Celery","type":"tags"},{"content":"","date":"2023.03.29","externalUrl":null,"permalink":"/tags/cpu/","section":"Tags","summary":"","title":"Cpu","type":"tags"},{"content":"","date":"2023.03.29","externalUrl":null,"permalink":"/tags/flower/","section":"Tags","summary":"","title":"Flower","type":"tags"},{"content":"","date":"2023.03.29","externalUrl":null,"permalink":"/tags/load-average/","section":"Tags","summary":"","title":"Load Average","type":"tags"},{"content":"","date":"2023.03.29","externalUrl":null,"permalink":"/tags/precess/","section":"Tags","summary":"","title":"Precess","type":"tags"},{"content":"","date":"2023.03.29","externalUrl":null,"permalink":"/tags/worker/","section":"Tags","summary":"","title":"Worker","type":"tags"},{"content":"열심히 데이터 분석 모델을 만들었다고 생각해보자. 이 모델을 이런저런 방식으로 다른 사람들도 사용할 수 있도록 만들고 싶은데 그러려면 뭔가 모델과 함께 작동할 화면 프로그램이 필요해보인다. 웹 어플리케이션은 화면을 바탕으로 개발한 기능을 원하는 시나리오대로 사용할 수 있는 하나의 프로그램이다. 그럼 웹어플리케이션에 대해 알아보자.\n웹어플리케이션 구조 예시\n-\u0026gt; Django를 활용한 웹어플리케이션\n먼저 웹어플리케이션 구조를 살펴보자. 파이썬 django를 사용해 만드는 웹어플리케이션 구조는 위 그림과 같다. 이 구조의 웹어플리케이션이 돌아가는 방식은 다음과 같다.\n1. 사용자가 PC 웹브라우저를 통해서 웹 어플리케이션에 특정행위에 대한 요청을 보낸다.\n* Web Browser(Chrome) -\u0026gt; Web Server(Nginx)\n2. 웹서버에서 사용자의 요청을 받음(Nginx)\n3. 웹서버가 WAS(Web Application Server)에 사용자들의 요청을 전달 함\n* Nginx -\u0026gt; uWSGI\n4. WAS가 어플레이션(별도 프로세스)에 들어온 요청들을 분산해 로직을 실행시킴\n* uWSGI -\u0026gt; Django(application)\n5. 어플리케이션에서 로직을 실행시키고(동적task, 비즈니스 로직), WAS(uWSGI)에 응답을 보냄(필요시 DB참조값을 보내겠지)\n* Application(Django) -\u0026gt; WAS(uWSGI)\n6. WAS가 어플리케이션(Django)으로부터 온 응답을 웹서버(Nginx)가 이해할 수 있는 형태로 변환해 전달\n* Application(Django) -\u0026gt; WAS(uWSGI) -\u0026gt; Web Server(Nginx)\n7. 웹서버(Nginx)가 정적 데이터와 함께, 사용자 웹브라우저상에 최종 응답결과를 제공함\n# 왜 Web Application을 사용할까?(왜 웹에서 동작할 수 있는 App으로 만드는거지?) # -\u0026gt; 그럼, web appication을 사용하지 않고 어떻게 머신러닝 모델을 사용하도록 만들 수 있을끼?\n$ python model.py data.csv parameter.csv sys 모듈의 sys.argv를 활용해 모델을 돌리는데 필요한 데이터와 파라미터를 넘기는 방식으로 프로그램을 구성할 수 있다.\n-\u0026gt; 즉, 목적에 부합하는 python파일을 만들고, 직접 커맨드를 입력해서 명령하여 동작하도록 할 수 있다.\n-\u0026gt; 또는 python의 pyQt와 같은 라이브러리를 통해서 GUI를 만들 수 있지만 UI디자인이 깔끔하지 않다.\n다만, 이렇게 만들어낸 프로그램은 내PC가 아닌 곳에서는 접근할 방법이 없기때문에, 타인이 해당 프로그램에 접근하고 사용할 수 있게끔 돕기 위해, Web Application을 사용하게 되는 것이고, 다른 컴퓨터에서도 서비스(모델)에 접근하기 위해서 서버 클라언트 구조가 필요하게 된다.)\n서버와 클라이언트\n서버는 요청을 받아 서비스나 데이터를 제공하는 컴퓨터\n클라이언트는 요청을 보내는 컴퓨터 또는 어플리케이션\n-\u0026gt; 서버와 클라이언트 구조를 갖춘 프로그램을 만들면 직접 개발한 컴퓨터가 아닌 다른 컴퓨터에서도 서비스(또는 모델)를 실행시키고 결과를 볼 수있다.\n여기서 클라이언트가 서버에 요청하는 것을 \u0026ldquo;Request\u0026quot;라고 하고,\n서버가 클라이언트에 결과를 제공하는것을 \u0026ldquo;Response\u0026quot;라고 한다.\n다만, 웹어플리케이션의 서버와 클라언트 통신에는 정해진 프로토콜이 있다. 이 프로토콜에 맞게 요청해야 결과가 오는데, 이에 대해 자세히 알아보자\nHTTP 프로토콜, REST API\nWeb Application 에서 서버와 클라이언트간 통신은 HTTP(HyperText Transfer Protocol)을 따른다.\nHTTP프로토콜을 따라 데이터를 어떤방식으로 요청할지 정할 수 있고 이에 따라 응답이 생성된다.\n1) HTTP Request\nHTTP 프로토콜에서는 URI(Uniform Resource Identifier)라는 주소에 메소드를 통해 데이터를 요청한다. 하나의 자원에 대해 단순 조회를 해본다거나 그 자원을 사용해 다른 결과를 만드는 등 다양한 행위를 하고 싶을 수 있기 때문에 HTTP에서는 여러 메소드를 두어 요청을 할 수 있게 만들어놓았다.\nHTTP 메소드의 종류는 다음과 같다.\n**GET:**URI에 데이터를 조회하는 메소드\nPOST: URI에 데이터를 생성하기 위한 메소드. 또는, URI에 데이터를 보내고 새로운 결과를 얻기 위한 메소드.\nPUT: URI 데이터 수정을 위한 메소드\n**DELETE:**URI 데이터 삭제를 위한 메소드\n이 외에도 HEAD, CONNECT, OPTIONS, TRACE, PATCH와 같은 메소드가 존재한다. 가장 많이 사용하는 메소드는 GET과 POST로 두 가지 메소드만 잘 알아둬도 많은 문제 상황을 해결할 수 있다.\n2) HTTP Response\n서버에 request를 보내면 response로 결과가 나온다. 크게 응답코드로 요청에 대한 성공/실패 등 수행 결과를 제공한다. 가끔 우리가 보는 404 에러 같은 메세지는 응답코드로 실패를 나타낸다. 400번대는 실패 200번대는 성공이라고 일단 알아두면 이해에 도움이 될 것이다. 요청에 대한 결과는 body에 json이나 정해진 형식으로 응답코드와 함께 제공된다.\nREST(Representational State Transfer) API\nREST API를 간단히 이야기하면, \u0026ldquo;위의 HTTP 메소드를URL주소로 Request를 쏠 수 있게 만든 인터페이스\u0026quot;이다.\n대부분의 웹 API설계는 REST API방식으로 이루어지므로 웹이서 데이터를 주고 받을 수 있는 수단 정도로 이해하면 되겠다.\n기타\n웹어플리케이션에서 데이터의 교환은 주로 json을 이용해 이루어진다.\n어플리케이션(ex.Django)\n서버와 클라이언트, HTTP통신에 대해 알아봤으니, Application은 어떻게 만들어지는지 알아보자.\nMTV (Model - Template -View) 패턴\n-\u0026gt; Django MTV패턴\n웹어플리케이션은 여러화면과 이에 관련된 로직, 데이터를 바탕으로 만들어지는데, 현대 웹어플리케이션에는\n이런 화면, 로직, 데이터를 효과적으로 관리하기 위해 소프트웨어 패턴(규칙)을 가지고 개발된다.\n파이썬 Django는 MTV패턴을 사용한다.\n간단히 MTV 패턴에 대해 알아보자.\nModel: 데이터를 다루는 부분. 데이터가 정의되고 DB와 연결됨 Template: 화면을 다루는 부분. 화면에 표시될 html을 다룸 View: 로직을 다루는 부분. HTTP request와 연관된 로직을 관리하고 처리 1) View\nMTV 패턴에서 사용자의 요청은 View에서 로직으로 구현\nView는 Model로부터 요청을 처리하기 위한 데이터를 받아오고 Template의 html에 로직의 결과를 포함해 응답\n만약 머신러닝 모델을 구현했다면, 구현한 함수나 클래스가 View에서 호출된다.(모델을 호출하는내용이 VIew에서 시작한다 정도?!)\nModel로 부터 필요한 설정값이나 데이터가 있다면 받아오고, 정의된 Template을 바탕으로 화면에 표시해준다.\n2) MVC패턴을 이용해 만든 Django 프로젝트 예시\n. ├── config # 설정 │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── core # 공통 모듈 (비슷한 기능을 다른 앱에서 참조하기 위해 사용) │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ ├── models.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── reviews # 개별 앱 (리뷰) │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ ├── models.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── rooms │ ... ├── templates # html │ ├── rooms │ │ ├── room_detail.html │ │ ├── ... │ │ └── room_edit.html │ │ ... | ├── 404.html │ └── base.html ├── static # 정적데이터 (nginx 웹서버 이용 시 nginx가 참조하는 다른 위치에 존재) │ ├── css │ └── image ├── db.sqlite3 # 데이터베이스 (mysql, postgresql 등 사용하면 별도로 연결) ├── manage.py └── requirements.txt Django 프로젝트는,\n1) 설정관련 디렉토리 : settings.py, urls.py, wsgi.py 포함\n2) 개별 앱 디렉토리 : reviews, rooms 등\n3) Templates 디렉토리 : 화면에 표시하기 위한 html\n4) static 디렉토리(정적 데이터포함)\n5) 데이터베이스 등\n으로 구성된다.\nDjango 작동방식\nHTTP Request을 보냄\nHTTP Request의 URL을 보고 어떤 View로 찾아가야하는지 urls.py에서 찾아 넘겨줌\nView에 작성된 로직에 따라 Model 데이터를 조작하고 필요한 값을 읽어옴\nModel에서 읽어온 값과 Template의 html 형식을 조합해 완성된 HTML HTTP Response 생성\n작동 방식에 나온 구성 요소들의 코드 예시를 살펴보자.\nurls.py\nfrom django.urls import path from . import views app_name = \u0026#34;reviews\u0026#34; urlpatterns = [ path(\u0026#34;create/\u0026lt;int:room\u0026gt;\u0026#34;, views.create_review, name=\u0026#34;create\u0026#34;) ] 어떤 URL이 어떤 View에 맵핑되어있는 지를 작성한다. path(\u0026ldquo;create/int:room\u0026quot;, views.create_review, name=\u0026ldquo;create\u0026rdquo;) 에서 \u0026ldquo;create/int:room\u0026rdquo; 부분이 URL, views.create_review 부분이 View를 의미한다.\nmodel.py\nfrom django.db import models from django.core.validators import MinValueValidator, MaxValueValidator from core import models as core_models class Review(core_models.TimeStampedModel): \u0026#34;\u0026#34;\u0026#34; Review Model Definition\u0026#34;\u0026#34;\u0026#34; review = models.TextField() accuracy = models.IntegerField( validators=[MinValueValidator(1), MaxValueValidator(5)] ) communication = models.IntegerField( validators=[MinValueValidator(1), MaxValueValidator(5)] ) cleanliness = models.IntegerField( validators=[MinValueValidator(1), MaxValueValidator(5)] ) location = models.IntegerField( validators=[MinValueValidator(1), MaxValueValidator(5)] ) check_in = models.IntegerField( validators=[MinValueValidator(1), MaxValueValidator(5)] ) value = models.IntegerField( validators=[MinValueValidator(1), MaxValueValidator(5)] ) user = models.ForeignKey(\u0026#34;users.User\u0026#34;, related_name=\u0026#34;reviews\u0026#34;, on_delete=models.CASCADE) room = models.ForeignKey(\u0026#34;rooms.Room\u0026#34;, related_name=\u0026#34;reviews\u0026#34;, on_delete=models.CASCADE) def __str__(self): return f\u0026#34;{self.review} - {self.room}\u0026#34; def rating_average(self): avg = ( self.accuracy + self.communication + self.cleanliness + self.location + self.check_in + self.value ) / 6 return round(avg, 2) rating_average.short_description = \u0026#34;Avg.\u0026#34; class Meta: ordering = (\u0026#34;-created\u0026#34;,) 어떤 객체가 어떤 방식으로 만들어져야 하는지를 규정한다. 예시에서 Review라는 객체는 텍스트로 된 리뷰 필드, accuracy 등 항목에 대한 개별 점수 필드로 정의되고 foreign key를 통해 리뷰 작성자와 리뷰가 작성되는 방에 연결된다. 파이썬 코드로 위와 같이 작성하면 ORM(Object Relational Mapping)이라는 기술을 통해 DB와 연동되어 쿼리를 사용할 필요 없이 파이썬 코드로 데이터를 조작할 수 있게 된다. Model로 만들어놓은 객체들은 추후 View에서 import해 사용한다.\nview.py\nfrom django.contrib import messages from django.shortcuts import redirect, reverse from rooms import models as room_models from . import forms def create_review(request, room): if request.method == \u0026#34;POST\u0026#34;: form = forms.CreateReviewForm(request.POST) room = room_models.Room.objects.get_or_none(pk=room) if not room: return redirect(reverse(\u0026#34;core:home\u0026#34;)) if form.is_valid(): review = form.save() review.room = room review.user = request.user review.save() messages.success(request, \u0026#34;Room reviewed\u0026#34;) return redirect(reverse(\u0026#34;rooms:detail\u0026#34;, kwargs={\u0026#34;pk\u0026#34;: room.pk})) View에서는 로직을 정의한다. def create_review(request, room) 으로 리뷰를 생성하기 위한 로직을 정의하고 return redirect(reverse(\u0026ldquo;rooms:detail\u0026rdquo;, kwargs={\u0026ldquo;pk\u0026rdquo;: room.pk})) 으로 원하는 Template으로 이동시킨다.\nroom_detail.html\n{% extends \u0026#34;base.html\u0026#34; %} {% load is_booked on_favs %} {% load i18n %} {% block page_title %} {{ room.name }} {% endblock page_title %} {% block content %} ... \u0026lt;div class=\u0026#34;-mt-5 container max-w-full h-75vh flex mb-20\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;h-full w-1/2 bg-center bg-cover\u0026#34; style=\u0026#34;background-image:url({{room.first_photo}})\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;h-full w-1/2 flex flex-wrap\u0026#34;\u0026gt; # {% for photo in room.get_next_four_photos %} \u0026lt;div style=\u0026#34;background-image:url({{photo.file.url}})\u0026#34; class=\u0026#34;w-1/2 h-auto bg-cover bg-center border-gray-500 border\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; # {% endfor %} \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;container mx-auto flex justify-around pb-56\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;w-1/2\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;flex justify-between\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;mb-5\u0026#34;\u0026gt; \u0026lt;h4 class=\u0026#34;text-3xl font-medium mb-px\u0026#34;\u0026gt;{{ room.name }}\u0026lt;/h4\u0026gt; \u0026lt;span class=\u0026#34;text-gray-700 font-light\u0026#34;\u0026gt;{{ room.city }}\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;a href=\u0026#34;{{ room.host.get_absolute_url }}\u0026#34; class=\u0026#34;flex flex-col items-center\u0026#34;\u0026gt; {% include \u0026#34;mixins/user_avatar_info.html\u0026#34; with user=room.host %} \u0026lt;span class=\u0026#34;mt-3 text-gray-500\u0026#34;\u0026gt;{{ room.host.first_name }}\u0026lt;/span\u0026gt; \u0026lt;/a\u0026gt; \u0026lt;/div\u0026gt; ... {% endblock content %} Template은 django 객체를 사용해 만든 html이다. django template만의 특수문법과 View에서 나온 값들을 포함해 사용자가 볼 html 페이지를 만든다.\ndjango도 하나의 거대한 프레임워크라 직접 사용하려면 배워야할 개념들이 많다. 처음 접한다면 Django Girls Tutorial을 따라해보자. 조금 더 실제 프로젝트에 가까운 케이스를 보고 싶다면 노마드코더 풀스택 에어비앤비 클론코딩를 보는 것을 추천한다.\n데이터 분석에서 django를 주로 사용했을 때는 모델을 API 형식으로 만들 때였던 것 같다. 전체적인 어플리케이션 만드는 것보다 API 만드는 법만 알고 싶다면 Django REST Framework Full Course For Beginners을 참조해보자.\n웹 서버, 웹어플리케이션 서버(WAS)\n어플리케이션이 만들어졌으면 HTTP 요청을 주고 받는 것을 관리해줄 부분이 필요하다. 웹서버와 웹어플리케이션서버(WAS)가 이 역할을 수행하는데 각자의 역할을 통해 시스템으로 들어오는 사용자 요청을 분배하고 관리한다.\nWeb Server(예. Nginx)\n이미지나 css, 단순 html 등의 정적 데이터를 처리하는 서버이다. Nginx와 Tomcat 등이 웹서버에 속한다. 클라이언트가 요청을 하면 웹서버에서 제일 먼저 HTTP 요청을 처리하며 정적 데이터는 웹서버에서 제공하고 동적으로 처리가 필요한 데이터는 WAS에 넘긴다.\nWAS (Web Application Server) (예. uWSGI)\n웹서버로 온 HTTP 요청을 여러 어플리케이션에 분배해 부하를 관리하는 서버이다. WAS는 웹서버로부터 받은 요청을 중간에 해석해 django 어플리케이션으로 전달한다. 설정한 프로세스 갯수만큼 어플리케이션 객체를 띄우고 이들에게 요청을 분배해 많은 요청이 들어왔을 때 장애가 일어나지 않고 응답을 보낸다.\nWAS도 정적 데이터 처리가 가능해서 python [manage.py](http://manage.py) runserver 로 실행하는 django 개발 서버만으로도 웹어플리케이션을 구동시킬 수 있다. 다만, 정적 데이터는 Nginx 같은 웹서버에서 동적으로 처리가 필요한 데이터를 WAS를 통해 분배하는 구조가 더 효율적이다.\ndjango의 WAS에는 gunicorn과 uwsgi 등이 있다. django 개발 서버를 사용하면 개발 변경 사항을 반영하기 위한 부분을 제외하면 하나의 어플리케이션이 돌아가 많은 요청을 보내면 버벅거린다. gunicorn이나 uwsgi 설정에서 프로세스 갯수를 늘리면 대기하고 있는 어플리케이션들이 각각 요청을 수행해 효과적으로 요청을 처리할 수 있다.\ndjango의 WAS 부분이 이해하기 어려웠었다. 여러 프로세스로 django 어플리케이션을 띄우고 Nginx로부터 받은 요청을 어플리케이션에 나눠 부하를 관리하는 서버라고 생각하면 좋을 것 같다.\nReferences\nhttps://bunnnybin.tistory.com/entry/웹-서비스의-구조 https://www.altexsoft.com/blog/machine-learning-pipeline/ https://www.fullstackpython.com/green-unicorn-gunicorn.html https://www.fullstackpython.com/wsgi-servers.html https://stackoverflow.com/questions/58860877/what-is-the-lifetime-of-a-django-wsgi-application-object-when-served-using-gunic https://uwsgi-docs.readthedocs.io/en/latest/WSGIquickstart.html https://nitro04.blogspot.com/2020/01/web-types-of-web-application-server-and.html https://twowix.me/85 https://docs.djangoproject.com/en/3.2/ref/django-admin/ https://soniacomp.medium.com/장고-django-멀티스레드-와-node-js-싱글-스레드-202b3270bf95 https://developer.mozilla.org/ko/docs/Learn/Server-side/Django/Introduction https://nomadcoders.co/airbnb-clonehttps://jinwoo1990.github.io/dev-wiki/web-application/ ","date":"2023.03.26","externalUrl":null,"permalink":"/posts/43/","section":"글","summary":"","title":"[WAS]Django를 활용한 웹 어플리케이션 \b구조 분해","type":"posts"},{"content":"서비스에 설정된 Default Git branch를 조회하고,\n다른 branch를 기본 branch로 변경해보자.\n[Git Branch 조회]\ngit branch -a git branch가 \u0026ldquo;develop\u0026quot;가 default로 셋팅되어있다.\n[Git Branch 변경]\ngit checkout master \u0026ldquo;\bmaster\u0026quot;를 default git branch로 셋팅하였다.\n","date":"2023.03.24","externalUrl":null,"permalink":"/posts/42/","section":"글","summary":"","title":"[Git]Branch 조회 \u0026 기본 Branch 변경","type":"posts"},{"content":"","date":"2023.03.24","externalUrl":null,"permalink":"/tags/branch/","section":"Tags","summary":"","title":"Branch","type":"tags"},{"content":"","date":"2023.03.24","externalUrl":null,"permalink":"/tags/checkout/","section":"Tags","summary":"","title":"Checkout","type":"tags"},{"content":"오늘은\nWEB서버와 WAS서버를 각각 정의하고, 차이점과 활용방식에 대해 간단히 정리하고자 한다.\nWEB(Web Server)이란?\n작성된 HTML페이지등을 네트워크망에 종속되지 않고, 웹서비스를 할 수 있도록 하는 어플리케이션으로, 소프트웨어와 하드웨어로 이루어진 서버로 구분될 수 있고 WEB서버는 \u0026lsquo;정적 파일\u0026rsquo;을 처리하는데 중점을 두는 서버이다.\n여기서 \u0026lsquo;정적파일\u0026rsquo;이라 함은 HTML, CSS, JavaScript, 이미지와 같이 정의된 내용의 파일을 포함한다.\n그리고, 웹서버는 클라이언트의 요청을 받아 해당 요청에 따라 정적파일을 반환한다.\n(1) 소프트웨어 웹서버 : 웹 브라우저 클라이언트로부터 HTTP요청을 받아들이고, HTML문서(정적파일 처리)등을 내용에 따라처리하고 반응하는 컴퓨터 프로그램\n(2) 하드웨어 웹서버 : 위 언급된 기능을 제공하는 컴퓨터 프로그램을 실행하는 컴퓨터를 의미\nWAS(Web Application Server)란? WEB서버 + WEB컨테이너 WAS는 반대로 \u0026lsquo;동적컨텐츠\u0026rsquo;를 처리하는데 중점을 둔 서버이다.\n이는 서버 측 스크립트와 데이터베이스를 활용하여 **\u0026ldquo;클라이언트의 요청에 따라 동적으로 생성된 웹페이지를 제공하는것\u0026rdquo;**을 포함한다.\nWAS WEB Server tomcat, tMax jeus, BEA Web Logic, IBM Webspere, JBOSS, Bluestone, Gemston, insprise, Oracle, PowerTier, Apptivity, silverStream IIS, apache, tMax, WebtoB 클라이언트 - web server - WAS 의 구조를 표현\n두 서버의 처리방식은?\n1) WEB서버\n웹서버는 HTTP\b프로토콜을 사용하여 클라이언트 요청을 처리하고, 정적파일을 반환한다.\n웹서버는 동적 콘텐츠 처리에 필요한 \u0026lsquo;추가 리소스\u0026rsquo;나 \u0026lsquo;런타임 환경\u0026rsquo;을 제공하지 않는다.\n2) WAS\nWAS는 WEB서버의 기능과 함께, 동적 콘텐츠 처리를 수행한다.\n이를 통해 서버 측 로직을 수행하고, 데이터베이스에 액세스하며, 사용자 인터페이스를 동적으로 생성할 수 있다.\n주로 데이터베이스 서버와 같이 수행된다.\n웹서버 + 웹컨테이너로 웹 상에서 사용하는 컴포넌트를 올려놓고 사용하게 되는 서버이다.\n-\u0026gt; 기본기능\n* 프로그램 실행 환경과 데이터베이스 접속기능을 제공한다.\n* 여러개의 트랜잭션을 관리한다.\n* 업무를 처리하는 비즈니스 로직을 수행한다(동적 콘텐츠 처리 담당)\n* Web service 플랫폼으로서의 역할\n3) 통합 구성(일반적인 사용방식)\n일반적으로는 웹 애플리케이션의 성능과 보안을 개선하기 위해서 WEB서버와 WAS를 함께 사용하는 경우가 많다.\n웹 서버는 정적콘텐츠(HTML, CSS, JavaScript, 이미지)를 처리하고, 동적요청(DB를 참조하는 등의 비즈니스 로직)을 WAS로 전달한다. 이렇게 구성하면 웹 서버가 정적파일처리에 최적화되고, WAS는 동적 콘텐츠 처리에 집중할 수 있어 전체적인 성능 향상이 가능하다.\n예를들면, NginX와 Django를 사용할때, Nginx는 Web서버의 역할을 하고 Django는 WAS의 역할을 한다고 가정한다.\n두개의 서비스는 별개의 프로세스로 실행되며, Nginx는 동적 요청(비즈니스로직)을 Django로 프록시 한다.이때, WAS(Django)로 요청을 전달하는것은 프록시 또는 리버스 프록시를 사용하여 HTTP 프로토콜을 통해 전달하며, 당연히, WEB서버는 WAS의 IP와 port를 통해 header와 body의 형태로 요청을 전달하게 된다.\n결론\nWEB \u0026amp; WAS 통합 구동방식\n쉽게 정리하면, WEB container의 유무로 WEB server와 WAS를 구분할 수 있고,\nWEB server는 HTML, CSS, 이미지와 같은 정적 파일을 읽는데 특화된(빠른) 서버라고 할 수 있고,\nWAS는 정적 파일 처리는 WEB보다 느리고 비효율적이며, 동적컨텐츠(비즈니스로직)처리에 특화된 서버로 이해할 수 있다.\n우리가 사용하는 웹페이지는 정적파일과 동적 컨텐츠가 함께 동작해야하는데,\nWEB(정적파일읽기)과 WAS(동적컨텐츠, 로직처리)에게 각각 잘하는 task를 처리하도록 하여야, 성능을 극대화 할 수 있다.\n진행원리는 사용자가 클라이언트(브라우저)에 요청하게 되면, 이를 웹서버에서 반응하여 WAS의 처리를 거쳐 웹페이지로,\n다시 웹서버에서 클라이언트(브라우저)에 응답메세지를 주는 방식으로 처리하게 된다.\n추가적으로, JSP를 배울때, 대체적으로 Apache와 Tomcat을 사용한다.\nASP를 구동하기 위해서는 IIS를 사용한다.\nASP의 환경이 MS계열 서비스라면, WAS의 기능을 담당하는 부분이 있어 별도로 WAS가 필요하지 않다.\nWAS와 WEB서버 사용방식별 장단점\n1) WAS와 Web Server를 분리하지 않는 경우\n모든 컨텐츠를 한곳에 집중시켜 웹서버와 WAS의 역할을 동시에 수행,\n스위치를 통한 로드 밸러싱으로 처리\n-\u0026gt; 사용자가 적을 경우 효율적\n2) WAS와 Web Server를 분리한 경우\n웹서버와 WAS의 기능적 분류를 통해 효율적인 분산을 유도,\n정적인 데이터는 웹서버에서, 동적인 데이터는 WAS에서 처리\n3) WAS 여러개와 Web Server를 분리한 경우\nWAS단을 프리젠테이션 로직과 비즈니스 로직으로 구분하여 구성.\n특정 logic의 부하에 따라 적절히 대응할 수 있지만, 설계단계와 유지보수 단계가 복잡해질 수 있다.\n[ WAS와 Web Server를 분리하는 이유 ]\n기능을 분리하여 서버의 부하 방지\n물리적으로 분리하여 보안강화\n여러대의 WAS를 연결가능(로드밸런싱의 역할 및 fail over, fail back 처리에 유리)\n여러 웹어플리케이션을 서비스 가능(java 서버, c#서버, php서버 등 하나의 웹서비스를 통해 서비스 가능)\n* 참고\nhttps://helloworld-88.tistory.com/71\n","date":"2023.03.23","externalUrl":null,"permalink":"/posts/41/","section":"글","summary":"","title":"[WEB\u0026WAS]WEB서버와 WAS에 대해서 알아보자","type":"posts"},{"content":"","date":"2023.03.23","externalUrl":null,"permalink":"/tags/apache/","section":"Tags","summary":"","title":"Apache","type":"tags"},{"content":"","date":"2023.03.23","externalUrl":null,"permalink":"/tags/tomcat/","section":"Tags","summary":"","title":"Tomcat","type":"tags"},{"content":"","date":"2023.03.23","externalUrl":null,"permalink":"/tags/was/","section":"Tags","summary":"","title":"Was","type":"tags"},{"content":"","date":"2023.03.23","externalUrl":null,"permalink":"/tags/web/","section":"Tags","summary":"","title":"Web","type":"tags"},{"content":"","date":"2023.03.23","externalUrl":null,"permalink":"/tags/webserver/","section":"Tags","summary":"","title":"Webserver","type":"tags"},{"content":"","date":"2023.03.21","externalUrl":null,"permalink":"/tags/.idea/","section":"Tags","summary":"","title":".Idea","type":"tags"},{"content":"최초 pycharm Preference 설정 시 가상환경 셋팅이 완료되었다면, 가장 먼저 interpreter 설정을 진행해야한다.\n이때, 간혹 project의 root 디렉토리를 pycharm으로 정상연결 완료 했음에도,\npreference에서 Django셋팅을 할 수 있는 화면이 나오지 않는 경우가 있다, 즉 좌측 영역이 공백으로 노출되는\n현상인데, 뭔가 셋팅을 잘못한줄 알고, 가상환경 생성 및 pycharm을 여러번 진행하며 많은 시간을 삽질하며 보냈다가\n겨우 해결하여, 해결 내용을 포스팅 한다.\n(이미, 해결을 해버려서, preference에서 Django가 노출되지않는 화면을 미처 캡쳐하지 못했다\u0026hellip;.)\n[확인순서]\n1) 가장 먼저, 가상환경의 위치가 프로젝트 root 디렉토리에 제대로 생성되어 있는지 확인한다!\n2) Django가 설치되어 있는지 확인한다!\n: 새로 생성한 가상환경(venv)의 경우, Django가 설치되어 있지 않아, configuration을 진행하지 못하는 경우(new configuration 목록에 Django server가 비활성화)가 있다. requirements.txt를 통해, Django를 반드시 설치한 이후 진행할 수 있도록 한다.\n3) python interpreter에서 생성한 가상환경의 python 을 선택한다.\n여기까지 진행이 완료되었다면, Preference\u0026gt; Langugages\u0026amp;Frameworks \u0026gt; Django에서 프로젝트의 root 디렉토리를 연결할 수 있어야 하는데, 이 부분이 노출되지않는 경우가 발생할 수 있다.\n[해결방법]\n1) 가장 먼저, Preference 좌측 목록에서 \u0026ldquo;project:\u0026ldquo;프로젝트명\u0026quot;\u0026ldquo;이 노출되는지 확인한다.\n만약, 프로젝트와 하위의 python interpreter, project structure메뉴가 노출되지 않는다면,\npycharm에서 root 디렉토리를 인식하지 못하는것이다.\n2) pycharm을 종료하고,\n3) pycharm 재 실행 시, root 디렉토리(venv가 포함되어있는 root 폴더)를 잘선택해서 열었는지 확인한다.\n재실행해도 동일하다면,\n4) Teminal을 통해 (ls -al) .idea 폴더의 사본을 만들어 둔 후 pycharm을 종료한다.\n(*.idea는 pycharm에서 디렉토리를 인식하는 정보를 담고있다.)\n5) .idea폴더를 삭제한다. 6) 다시 pycharm 재실행하면 idea 폴더가 자동 재생성되며 정상노출된것을 확인할 수 있다. (.idea가 초기화되어 재생성된것)\n7) 다시 preference를 확인해보면, 프로젝트 인식 및 Preference\u0026gt; Langugages\u0026amp;Frameworks \u0026gt; Django에서 셋팅이 가능한 화면으로 바뀐것을 확인할 수 있다.\n아무래도 이 문제는 pycharm 오류인듯하다.\npycharm 최초 설정 시, 알 수 없는 이유로 root 폴더를 인식하지 못한채로 .idea가 생성되면, 그 이후에 재실행하더라도 내용이 업데이트되지않아, 올바른 root 폴더를 인식하지 못하는 문제인듯하다.\n무튼 동일 현상이 있을 경우 위 방법으로 해결하도록 하자.\n","date":"2023.03.21","externalUrl":null,"permalink":"/posts/40/","section":"글","summary":"","title":"[Pycharm 설정]Preference에서 Django설정 불가 이슈","type":"posts"},{"content":"","date":"2023.03.21","externalUrl":null,"permalink":"/tags/interpreter/","section":"Tags","summary":"","title":"Interpreter","type":"tags"},{"content":"","date":"2023.03.21","externalUrl":null,"permalink":"/tags/preference/","section":"Tags","summary":"","title":"Preference","type":"tags"},{"content":"","date":"2023.03.21","externalUrl":null,"permalink":"/tags/setting/","section":"Tags","summary":"","title":"Setting","type":"tags"},{"content":"","date":"2023.03.21","externalUrl":null,"permalink":"/tags/vm/","section":"Tags","summary":"","title":"Vm","type":"tags"},{"content":"보통 간단한 서비스를 POC할때, docker container를 하나 만들어, 개발을 진행하곤 한다.\n하지만, 여러개의 애플리케이션과 데이터베이스를 구성해서 사용해야할때, 각 애플리케이션과 데이터베이스는 서로 다른 환경과 종속성을 가지고 만들어져야할때가 있다.\n이때, 만약 각 애플리케이션이 실행된 container를 별도로 관리하고 모니터링 해야한다면 매우 불편할 수 있을것이다.\n이와 같이 복수의 docker container를 운영할때, 이를 동시에 실행, 관리 하기 위한 도구인 docker compose를 알아보려한다.\nDocker Compose란? # Docker 기반의 multi container 애플리케이션을 \u0026ldquo;정의\u0026rdquo;, \u0026ldquo;실행\u0026rdquo;, \u0026ldquo;관리\u0026quot;하기 위한 도구이다. Docker Compose의 장점? # 웹 애플리케이션의 경우 웹 서버 컨테이너와 데이터 베이스 컨테이너 등 여러개의 컨테이너로 구성되는 경우가 많은데, Docker Compose를 사용하면 각 컨테이너의 구성 정보를 한 곳에서 관리할 수 있으며 컨테이너간의 의존성을 자동으로 처리하여 애플리케이션을 쉽게관리할 수 있다. 여러개의 Docker 컨테이너를 한번에 실행하고, 각 컨테이너의 네트워크 연결 및 데이터 볼륨 공유를 자동으로 처리할 수 있음 여러 컨테이너를 동시에 실행하거나 컨테이너간의 의존성 관리를 자동으로 처리할 수 있음 웹 서버 컨테이너와 데이터베이스 컨테이너를 따로 구성하는 이유는? # 웹 서버와 데이터베이스의 경우 각각 요구 사항이 다르고, 별개의 종속성을 가지고 분리되어 운영되어야 하기 때문이다. 웹서버와 데이터 베이스 컨테이너의 주요 요구사항 예시는 아래와 같다.\n컨테이너 구분컨테이너 생성 시 주요 요구사항\n컨테이너 구분 컨테이너 생성 시 주요 요구사항 웹서버 컨테이너 특정 포트 사용 HTTP요청을 수신하기 위해 특정 포트를 사용해야하며, 이 포트는 컨테이너와 호스트간의 포트 매핑을 통해 호스트에서도 접근이 가능해야한다. 파일 또는 코드 공유 웹서버 컨테이너에서 실행되는 애플리케이션은 웹페이지, 이미지, 자바스크립트 파일 등의 정적 파일(정적파일은 소스코드나 그 외 파일을 의미)을 제공할 수 있음. 이러한 정적 파일은 컨테이너 내부에 저장되어 있어야 하지만, 개발자는 이 파일들을 로컬 컴퓨터에서 편집하고 디버깅할 수 있어야함. 따라서, 호스트와 컨테이너간의 디렉토리를 공유(호스트의 디렉토리를 컨테이너로 마운트하는 행위를 의미)하는 것이 필요함 - 아래 docker run 명령어를 통해 호스트 디렉토리를 컨테이너로 마운트할 수 있지만, yaml파일을 통해 docker compose up하는 경우 굳이 docker run할 필요없음(volumns로 정의해놓으면 되기 때문) * docker run -v /path/to/host/directory:/path/to/container/directory image_name 데이터베이스 컨테이너 데이터베이스 소프트웨어 설치 데이터베이스 컨테이너에는 데이터 베이스 소프트웨어가 설치되어 있어야함(ex.MySQL, PostgreSQL, MongoDB 등) 데이터베이스 초기화 및 구성 - 데이터베이스 컨테이너 시작할 때, 데이터베이스를 초기화하고 구성해야함. - 이 과정에서 데이터베이스의 스키마와 테이블을 생성하고 초기 데이터를 적재함. 백업과 복구 - 데이터베이스 컨테이너에서는 데이터베이스의 백업과 복구 기능이 필요 - 이를 위해 데이터베이스 소프트웨어에서 제공하는 백업 및 복구 도구를 사용 데이터베이스 보안 데이터베이스 컨테이너는 중요한 데이터를 저장하므로, 보안이 중요. 따라서 암호화, 인증, 권한관리등을 통해 데이터베이스를 보호해야함. 해당 내용에 대한 정의 필요 성능 데이터베이스 실행 시 CPU, 메모리, 디스크 I/O등의 하드웨어 리소스등을 고려하여 정의 필요 확장성 데이터베이스 컨테이너는 데이터양이 증가하면 확장할 수 있어야 함. 샤딩(sharding), 레플리케이션(replication)과 같은 기술을 사용하여, 데이터베이스 확장이 가능하도록 정의 YAML? # 주로 파일 구성이나 데이터 전송을 위해 사용하는 파일의 한 형태로, docker에서는 해당 yaml파일 형식을 사용해서 애플리케이션의 구성요소를 정의한다. 일반적으로 텍스트 파일로 저장되며, 확장자는 .yaml 또는 .yml로 사용됩니다. yml파일은 들여쓰기를 통해 계층적인 데이터를 나타내며, 각 데이터 요소는 콜론(:)을 사용하여 이름과 값으로 구분됩니다. 들여쓰기는 탭 또는 공백(스페이스) 2~4개 사용이 권장됨 Docker Compose YAML 파일 예제 아래 YAML 파일은 다음과 같은 동작을 수행함 version: \u0026#39;3\u0026#39; #Compose 파일 버전을 지정합니다. 이 예제에서는 버전3을 사용합니다. services: # 애플리케이션의 각 서비스를 정의합니다. web: # 웹 서버를 정의합니다. build: . #Dockerfile에서 이미지를 빌드합니다. ports: # 호스트와 컨테이너간의 포트 매핑을 정의합니다. - \u0026#34;5000:5000\u0026#34; volumes: # 호스트와 컨테이너 간의 디렉토리 매핑을 정의합니다. - .:/code links: #Redis 서비스와 연결됩니다. - redis redis: #Redis 데이터 베이스를 정의합니다. image: \u0026#34;redis:alpine\u0026#34; #Docker Hub에서 이미지를 가져옵니다. \u0026lsquo;docker-compose up\u0026rsquo; 명령을 실행하여 웹 서버 및 Redis 데이터베이스를 시작합니다. \u0026lsquo;web\u0026rsquo; 서비스는 현재 디렉토리에서 Dockerfile을 사용하여 이미지를 빌드하고 호스트의 5000번 포트를 컨테이너의 5000번 포트로 매핑합니다. 현재 디렉토리를 컨테이너의 code 디렉토리에 마운트합니다. \u0026lsquo;redis\u0026rsquo; 서비스는 Redis:alpine 이미지를 사용하여 컨테이너를 시작합니다. \u0026lsquo;web\u0026rsquo;서비스는 \u0026lsquo;redis\u0026rsquo;서비스와 연결됩니다. 위 예제는 Docker Compose YAML 파일을 사용하여 두개의 서비스(\u0026lsquo;web\u0026rsquo;, \u0026lsquo;redis\u0026rsquo;)를 정의하는 방법을 보여줍니다. YAML파일을 사용하여 복잡한 애플리케이션을 구성하고 실행할 수 있습니다. ","date":"2023.03.21","externalUrl":null,"permalink":"/posts/39/","section":"글","summary":"","title":"[Docker]Docker Compose","type":"posts"},{"content":"","date":"2023.03.21","externalUrl":null,"permalink":"/tags/compose/","section":"Tags","summary":"","title":"Compose","type":"tags"},{"content":"","date":"2023.03.21","externalUrl":null,"permalink":"/tags/container/","section":"Tags","summary":"","title":"Container","type":"tags"},{"content":"","date":"2023.03.21","externalUrl":null,"permalink":"/tags/docker/","section":"Tags","summary":"","title":"Docker","type":"tags"},{"content":"","date":"2023.03.21","externalUrl":null,"permalink":"/tags/docker-compose/","section":"Tags","summary":"","title":"Docker-Compose","type":"tags"},{"content":"","date":"2023.03.21","externalUrl":null,"permalink":"/tags/yaml/","section":"Tags","summary":"","title":"Yaml","type":"tags"},{"content":"어떤 프로그램이 돌아가게 해주는 실행파일의 경로를 잡아주는걸 환경변수 세팅이라고 하는데,\n리눅스에는 매우 다양한 환경변수들이 있다.\n지정된 환경변수를 출력하는 명령어는 printenv이다.\nprintenv 환경변수들의 경로인 path를 출력하는 명령어는 echo $PATH 이다. path들은 전부 : (콜론)으로 구별되어 있다.\necho $PATH 위에서 알아야 할 것은, 리눅스에서 사용하는 명령어들도 결국 일종의 실행파일인데 명령어 입력 시 환경변수에 잡혀있는 PATH를 따라가서 해당 파일이 실행되는 원리로 명령어가 작동된다는 것이다.\n즉, mkdir, ls 등의 명령어도 결국 저 path 경로에 저장되어 있다는 얘기이다.\n이 점을 이용하면, 명령어들이 실행파일로 이뤄져있고, PATH는 그 실행파일들의 경로를 지정한것이라고 하였으니,\n우리가 어떤 프로그램을 만들고나서 환경변수에 그 프로그램의 경로를 등록시켜주면, 우리가 만든 명령어로 프로그램을 이용할 수 있다는 얘기가 된다.\n그럼 먼저 쉘 언어를 이용해 간단한 덧셈 프로그램을 만들어 보자\n콘솔에 vi add라고 입력하면 빈 텍스트 에디터가 생긴다. add는 만들 파일의 이름이다\nvi 는 일종의 텍스트 에디터로 다양한 기능을 갖추었으나 자세한 설명은 나중에 하도록 하고\n지금은 쉘 코딩을 위해 텍스트 편집으로만 이용하겠다.\n아무튼 이렇게 입력하고 나면 빈 창이 뜨는데 i를 입력하면 편집(insert)모드로 전환된다.\n여기에 아래의 코드를 동일하게 입력한다.\n#!/bin/bash sum=$(($1 + $2)) echo \u0026#34;$1 + $2 = $sum\u0026#34; 그리고 ESC를 누르면 편집모드가 종료되는데, 이때 :(콜론)을 입력하면 종료모드를 선택할 수 있게 된다.\n**:**이 입력된 종료모드에서 w는 다른이름으로 저장모드 이고, q는 저장 없이 종료, !는 강제 수행 명령어이다.\n이를 합쳐서 주로 :wq이런식이나, :wq! 이런식으로 많이 사용한다. 아무튼 저장후 종료를 해준다.\n여기까지 했으면 프로그램이 잘 동작하는지 테스트해보자.\n쉘 스크립트의 실행 명령어인 sh를 앞에 써주고, 프로그램의 이름인 add, 이어서 피연산자 2개를 띄어쓰기로 구분해서 입력해준다. ex) sh add 1 2\n잘 동작하는것 확인\n이제 본 목적으로 돌아와서 이 프로그램을 환경변수에 등록하여, 이 프로그램이 있는 폴더까지 와서 sh add 1 2와 같이 입력해서 실행하는 것이 아닌, 어디에서나 add 1 2와 같이 명령어처럼 바로 실행할 수 있게 해보자.\n환경변수를 새로 등록하거나 편집하는 명령어는 export PATH=새로 등록할 프로그램의 주소이다.\n그런데 한가지 주의해야 할 점은 위의 명령어처럼 바로 해버리면 기존 환경변수에 덮어쓰기가 된다는 점이다.\n따라서 기존 환경변수에 이어서 새 환경변수를 등록하기 위한 명령어는 export PATH=$PATH:새로 등록할 프로그램의 주소를 사용한다.\n위와 같이 제대로 PATH를 등록했다면 sh 명령어 없이 프로그램 이름인 add만으로도 어느 경로에서나 명령어처럼 사용할 수 있음을\n알 수 있다.\n여기까지 해서 printenv, 기초적인 vi사용법, PATH 설정법 등에 대해 알아보았다. 참고로 export를 통한 path 설정은 일시적인 것으로 로그아웃 하면 다시 기본 설정으로 돌아간다. 영구적인 path 저장법은 추후 소개할 예정이다.\n","date":"2023.03.16","externalUrl":null,"permalink":"/posts/37/","section":"글","summary":"","title":"[Linux] 환경변수 설정","type":"posts"},{"content":"","date":"2023.03.16","externalUrl":null,"permalink":"/tags/echopath/","section":"Tags","summary":"","title":"Echo$path","type":"tags"},{"content":"","date":"2023.03.16","externalUrl":null,"permalink":"/tags/%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98/","section":"Tags","summary":"","title":"환경변수","type":"tags"},{"content":"대용량의 torch data를 pickle file로 dump하려고할때, 4GB이상의 데이터에 대해 dump error가 발생했다.\n해결방법\n요때는 dump할때, 아래와 같이 \u0026ldquo;protocol\u0026rdquo; parameter를 추가해주면 된다.\nwith open(\u0026#39;/workspace/DBP/data_storage/wontae_kim/슈퍼북/dataset/taxsave_2000000_data.pkl\u0026#39;, \u0026#39;wb\u0026#39;) as f: pkl.dump(last_data, f, protocol=4) ","date":"2023.03.07","externalUrl":null,"permalink":"/posts/34/","section":"글","summary":"","title":"[Pickle file dump error]OverflowError: cannot serialize a bytes object larger than 4 GiB","type":"posts"},{"content":"\u0026ldquo;IndexError: Target 811 is out of bounds.\u0026rdquo;\n간단한 multi-layer perceptron 모델을 학습시키는 과정에서, 위 오류가 발생했다.\n모델이 예측한 클래스 레이블이 실제 데이터셋의 클래스 레이블 범위를 벗어날 때 발생하는 오류이다.\n예를 들어, 클래스 레이블이 0부터 9까지인데 모델이 10 이상의 값을 예측한 경우 발생할 수 있다.\n가능한 원인으로는 다음과 같은 것들로 요약해볼 수 있다.\n데이터셋의 클래스 레이블 개수(unique한 label개수)와 모델의 output_dim이 일치하지 않는 경우 데이터셋의 클래스 레이블 범위가 0부터 output_dim-1까지가 아닌 경우 학습 데이터에 레이블이 잘못 매핑된 경우 따라서, 데이터셋의 클래스 레이블 범위를 확인하고 모델의 output_dim을 이에 맞게 수정해보면 될 것이다.\n또한, 데이터 전처리를 통해 레이블이 잘못 매핑된 경우에는 이를 수정해야 한다.\n\u0026lt;해결방법\u0026gt;\n1번의 경우 말그대로 예측할 class의 개수가 (binary classification이라면 2개겠지) output dimension과 다를 경우 발생할 수 있기때문에, 맞춰주면 해결된다. 2번의 경우 클래스는 당연히 label encoder를 통해서 0~클래스개수-1의 정수 목록으로 변환되어야한다.(나는 이부분을 빼먹었었다) ","date":"2023.02.21","externalUrl":null,"permalink":"/posts/31/","section":"글","summary":"","title":"[ML]모델 학습 진행 시 \"IndexError: Target 811 is out of bounds.\" Error","type":"posts"},{"content":"","date":"2023.02.20","externalUrl":null,"permalink":"/tags/jupyter-notebook/","section":"Tags","summary":"","title":"Jupyter Notebook","type":"tags"},{"content":"Jupyter notebook 진입 시 out of memory 문제로 진입이 불가할때\n[재현과정]\nJupyter notebook 작업 중 메모리 부족문제로 페이지가 뻗었다.\n파일 목록에서 파일 클릭하여 재진입 시,\n진입이 안돼\u0026hellip;\n아래와 같이 오류코드 5번 반환하면서 페이지 진입이 불가하다. 오류코드 5번에 대해서 좀 자세히 설명해주면 좋겠건만\u0026hellip;\n\u0026lt;시도방법\u0026gt;\n처음엔 커널이 뻗은건가하고 터미널을 종료해봤다 -\u0026gt; 실패\n해당 파일을 복사해서 파일을 실행하면 되지않을까?(사실 저번엔 이렇게 해결했었다.) -\u0026gt; 실패\n-\u0026gt; 구글링 해보니, 가상메모리를 다시 설정해봐라,,,또는 캐시를 날려봐라, 재부팅 해봐라 별의별 조언이 있었지만, 모두 해결은 되지않았고\u0026hellip;\n\u0026lt;해결방법\u0026gt;\n문제는 해당 파일안의 작업에서 print문이 수천 수만 row가 발생하며, 뻗은상황으로 보이고, 이것은 커널을 꺼도 파일 실행자체가 되지않았다.\n이때, jupyter 파일을 json형태로 불러와서 다시 dump하면 모든 작업이 종료되어 코드만 그대로 dump되어 실행이 가능하다.\njson 파일로 불러온 후 다시 dump해서 파일을 실행한 결과, 잘 실행된다. jupyter file을 json load한 후 다시 dump\n결과 -\u0026gt;\n잘 불러와진다. 다시 일하자.\n","date":"2023.02.20","externalUrl":null,"permalink":"/posts/30/","section":"글","summary":"","title":"Jupyter notebook 파일 실행 오류(out of memory)","type":"posts"},{"content":"","date":"2023.02.20","externalUrl":null,"permalink":"/tags/out-of-memory/","section":"Tags","summary":"","title":"Out of Memory","type":"tags"},{"content":"현재 회사에서 제공하고 있는 메신저의 화상대화 서비스에서 \u0026lsquo;ZOOM\u0026rsquo;에서 제공하는 배경 제거(대체)기능 구현에 대한 업무를 맡게 되었다.\n사실, Instance Segmentation과 real-time 수준의 빠른 모델적용을 요구하는 부분에 있어 지금까지 경험이 부족했기 때문에,\n어려울 수 있지만, 이번 Task가 큰 경험이 될 것으로 생각하고, 기쁜마음으로 시작하게 되었다.\n일단 배경 제거 기능의 경우 1차적으로 화상대화에 참여하고있는 화자와 그 외의 영역을 분리할 수 있어야하고, 화상대화 내부에서 빠르게 동작해야하기 때문에\nInstance Segmentation 이 선행되어야하고, Real-time으로 제공되어야한다. 우선, 해당 과제에 대해 정보를 수집하기 시작했고,\n우선 Instance Segmentation model을 알아보던 중, OpenCV의 cv2.bgsegm.createBackgroundSubtractorMOG이라는\n배경추출 라이브러리를 알게되었고, 테스트 필요항목에 포함시켰다.\n두번째로는 Rembg라는 라이브러리인데, 이미지내에서 일명 \u0026lsquo;누끼따기\u0026rsquo;로 유명한 라이브러리이다.\nreal time frame각각에 대해 배경분할을 진행한 후 각각의 frame을 이어붙이는 방식은 어떨까 생각해봤다.\n그래서 위 라이브러리와 모델을 실행하는 간단한 파일을 만들어 로컬에서 성능을 시험해보았다.\nimport argparse import cv2 import numpy as np from rembg import remove from PIL import Image cam = cv2.VideoCapture(0) width = cam.get(cv2.CAP_PROP_FRAME_WIDTH) height = cam.get(cv2.CAP_PROP_FRAME_HEIGHT) count = cam.get(cv2.CAP_PROP_FRAME_COUNT) fps = cam.get(cv2.CAP_PROP_FPS) print(\u0026#39;가로: \u0026#39;, str(width)) print(\u0026#39;세로: \u0026#39;, str(height)) print(\u0026#39;총 프레임 수: \u0026#39;, str(count)) print(\u0026#39;FPS: \u0026#39; + str(fps)) cam.set(3,1280) #CV_CAP_PROP_FRAME_WIDTH cam.set(4,720) #CV_CAP_PROP_FRAME_HEIGHT #cam.set(5,0) #CV_CAP_PROP_FPS parser = argparse.ArgumentParser(description=\u0026#34;0.opencv사용 / 1.rembg사용\u0026#34;) parser.add_argument(\u0026#39;--method\u0026#39;, \u0026#39;-m\u0026#39;, required=True, help=\u0026#34;사용할 라이브러리를 알려주세요. 예)0 or 1\u0026#34;) args = parser.parse_args() if int(args.method) == 0: \u0026#34;\u0026#34;\u0026#34; opencv의 createBackgroundSubtractorMOG 라이브러리 활용하는 방법 \u0026#34;\u0026#34;\u0026#34; fgbg = cv2.bgsegm.createBackgroundSubtractorMOG(backgroundRatio=0.7, noiseSigma=0) \u0026#34;\u0026#34;\u0026#34; history=200: 히스토리 길이 nmixtures=5: 가우시안 믹스처의 개수 backgroundRatio=0.7: 배경 비율 noiseSigma=0: 노이즈 강도 (0=자동) \u0026#34;\u0026#34;\u0026#34; # fgbg = cv2.createBackgroundSubtractorMOG2(history=500, varThreshold=16, detectShadows=False) \u0026#34;\u0026#34;\u0026#34; history=500 #히스토리 개수 varThreshold=16 #분산 임계 값 detectShadows=True #그림자 표시 \u0026#34;\u0026#34;\u0026#34; while True: ret_val, img = cam.read() # 캠 이미지 불러오기 fgmask = fgbg.apply(img) condition = fgmask != 0 #배경으로 구해진 영역인 \u0026#39;0\u0026#39;인 fixel을 제외하고 모두 1로 치환해준다. fgmask[condition] = 1 for tensor in range(img.shape[2]): # RGB하나씩 꺼내서 배경으로 계산된(fgmask) 영역만 모두 0으로 치환한다. img[:,:,tensor] = img[:,:,tensor] * fgmask cv2.imshow(\u0026#34;Cam Viewer\u0026#34;,img) # 불러온 이미지 출력하기 # cv2.imshow(\u0026#39;bgsub\u0026#39;, fgmask) # cv2.imshow(\u0026#39;answer\u0026#39;, test) if cv2.waitKey(1) == 27: break # esc to quit elif int(args.method) == 1: \u0026#34;\u0026#34;\u0026#34; rembg 라이브러리를 활용해서 프레임당 변환으로 구현하는 방식 \u0026#34;\u0026#34;\u0026#34; while True: ret_val, img = cam.read() # 캠 이미지 불러오기 output = remove(img) cv2.imshow(\u0026#34;Cam Viewer\u0026#34;,output) # 불러온 이미지 출력하기 # cv2.imshow(\u0026#39;bgsub\u0026#39;, fgmask) # cv2.imshow(\u0026#39;answer\u0026#39;, test) if cv2.waitKey(1) == 27: break # esc to quit # ddd.resize(int(ddd.shape[0]/2),int(ddd.shape[1]/2), ddd.shape[2]) 1) 첫번째, cv2.bgsegm.createBackgroundSubtractorMOG 라이브러리 적용 결과\ncv2.bgsegm.createBackgroundSubtractorMOG라이브러리는 영상내에서 픽셀당 색상의 변화가 있을 경우를 인식객체, 없는 경우 배경으로 간주하여, 움직이는 object를 추출하는 라이브러리이다.\n속도는 Real time에 적합했지만, 이 때문에, 아무래도 계속해서 움직이지않는 한 정상적으로 객체를 추출할 수 없다는 문제가 있었다.\n2) 두번째, Rembg 적용 결과\n결과, 영역내에서 객체와 배경을 매우 깔끔하게 분리해내고있는것을 확인할 수 있다.\n하지만, 해당 모델 자체가 매우 무거운것으로 보여진다.\nreal-time으로 제공하기위해서는 FPS 30으로 재생할때, 끊김 없이 동작해야하지만, 매우 심하게 끊어지는것을 확인할 수 있었다.\nRembg의 연산량을 줄여보기 위해, 이미지의 해상도를 낮추고 Input하도록 처리해보거나 여러가지 시도를 해보았지만,\n속도에는 큰 변화가 없고, segmentation정확도만 낮아지는 현상을 보였다.\n결국, 성능을 유지하면서 속도문제를 극복하기는 힘들어 보였다.\n아무래도 위 방식으로 해결방안을 마련하기는 힘들것으로 보이고, 다음번에는 real-time에 적합한 가벼운 Instance segmentation model을 찾아봐야할 것 같다.\n","date":"2023.01.09","externalUrl":null,"permalink":"/posts/23/","section":"글","summary":"","title":"[Real-time Instance Segmentation] 화상대화 배경제거 (1)","type":"posts"},{"content":"","date":"2023.01.09","externalUrl":null,"permalink":"/tags/deeplearning/","section":"Tags","summary":"","title":"Deeplearning","type":"tags"},{"content":"","date":"2023.01.09","externalUrl":null,"permalink":"/tags/instance-segmentation/","section":"Tags","summary":"","title":"Instance Segmentation","type":"tags"},{"content":"","date":"2023.01.09","externalUrl":null,"permalink":"/tags/opencv/","section":"Tags","summary":"","title":"Opencv","type":"tags"},{"content":"","date":"2023.01.09","externalUrl":null,"permalink":"/tags/rembg/","section":"Tags","summary":"","title":"Rembg","type":"tags"},{"content":"","date":"2023.01.09","externalUrl":null,"permalink":"/tags/segmentation/","section":"Tags","summary":"","title":"Segmentation","type":"tags"},{"content":"","date":"2023.01.09","externalUrl":null,"permalink":"/tags/vision/","section":"Tags","summary":"","title":"Vision","type":"tags"},{"content":" 0. 사전 과정 # Git에 Repository를 생성한다. Git Repo URL을 복사해서, Source Tree \u0026gt; Local \u0026gt; New\u0026hellip; \u0026gt; \u0026ldquo;Clone from URL\u0026rdquo; 3.git URL을 입력하고, 해당 코드를 Pull 받기위한 Local 경로를 설정한 후 로컬 repo를 생성한다.\n1. \u0026lsquo;dev\u0026rsquo; 브랜치 추가하기 # master에 commit하기 전 dev라는 별도의 branch를 생성하여 코드를 관리하고자 한다.\n사용자별로 별도 브랜치를 생성하여 관리할 수도 있다.\n\u0026lsquo;dev\u0026rsquo; 입력 후 branch생성\n2. Remote에 \u0026lsquo;dev\u0026rsquo;브랜치를 추적하는 dev를 추가하기 # 아래의 REMOTES \u0026gt; origin \u0026gt; \u0026lsquo;dev\u0026rsquo;가 생성된것을 확인할 수 있다.\n","date":"2023.01.03","externalUrl":null,"permalink":"/posts/21/","section":"글","summary":"","title":"[Source Tree] Branch 추가 및 Remote push하기!","type":"posts"},{"content":" Hugging face에서 아래와 같이 pre-trained model을 불러와서 사용하려했다. jupyter notebook에서 관련 repo 설치를 위해 아래 코드 입력시, \u0026ldquo;git: \u0026rsquo;lfs\u0026rsquo; is not a git command\u0026rdquo; 오류가 발생했다. git-lfs가 설치되어있지 않아 발생하는 문제였다. 우선 설치를 위해 homebrew가 먼저 설치되어 있어야한다. MacOS (Using Homebrew) $ brew update $ brew install git-lfs homebrew설치 후 git-lfs가 정상적으로 설치완료된 것을 확인할 수 있다. ","date":"2023.01.02","externalUrl":null,"permalink":"/posts/20/","section":"글","summary":"","title":"[Hugging Face model load관련 오류] git: 'lfs' is not a git command","type":"posts"},{"content":"","date":"2022.12.20","externalUrl":null,"permalink":"/tags/%EB%82%A0%EC%A7%9C%EA%B3%84%EC%82%B0/","section":"Tags","summary":"","title":"날짜계산","type":"tags"},{"content":"","date":"2022.12.20","externalUrl":null,"permalink":"/tags/%EB%AA%87%EC%A3%BC%EC%B0%A8/","section":"Tags","summary":"","title":"몇주차","type":"tags"},{"content":"","date":"2022.12.20","externalUrl":null,"permalink":"/tags/%EC%95%8C%EC%93%B8%EC%8B%A0%EC%9E%A1/","section":"Tags","summary":"","title":"알쓸신잡","type":"tags"},{"content":"이번 주는 몇 주차일까?\n올해의 몇 번째 주일까? 오늘은 이번 달의 몇 주 차일까? 궁금할 때가 있습니다. 그런데 연, 월이 월요일부터 항상 시작하는 것은 아니어서 몇 주 차인 것을 셀 때 어떻게 세는지 기준을 잘 모르는 경우가 많아서 말하는 사람마다 다른 경우도 있습니다. 이 글에서는 이번 주가 몇 주차인지 세는 방법을 국제 표준 기준으로 알아보도록 할게요.\n오늘은 올해의 몇 주차 인가요? # https://today-week.netlify.app/\n[오늘은 몇주차?\n오늘은 올해의 몇 번째 주인가요?\ntoday-week.netlify.app](https://today-week.netlify.app/)\n먼저 오늘이 올해의 몇주차인지, 몇일째인지를 위 사이트에서 확인할 수 있습니다.\n그렇다면 몇 주차인지 세는 기준은 뭘까요? # 몇 주차인지 세는 기준은 국제기준이 있습니다. 날짜와 시간에 대한 국제 표준 ISO-8601이 있는데요. 이에 따르면 매 주의 시작일은 월요일이며, 매월의 첫 주는 과반수(4일 이상)가 포함된 주를 기준으로 합니다. 1주일은 7일이므로 과반수라면 월요일, 화요일, 수요일, 목요일이 들어있거나 목요일, 금요일, 토요일, 일요일이 들어있는 주가 됩니다. 그러니까, 의외로 주를 세는 기준은 월요일이 아닌 목요일이 되는 겁니다.\n목요일이 기준이라고? 그럼 이번 달의 몇 주차를 어떻게 센다는 거예요? # 아래와 같은 달의 첫 주는 12월의 첫 주인지 11월의 마지막 주인지 주 차로 따지면 좀 헷갈리게 되는데요. 이럴 경우 1일이 월,화,수,목 중에 있으면 그 주의 다음주가 이 달의 첫 주가 됩니다.\n또한, 1일이 금, 토, 일 중에 있다면 그 주(월요일부터 일요일까지)는 전 달의 마지막 주(이 달의 첫 주가 아님)가 됩니다. 이와 같은 방법으로 연이나 월의 몇 주차인지 확인할 수 있습니다. (예를 들어, 1월 1일이 금, 토, 일에 있는 경우 그 주는 전 년의 마지막 주가 됩니다.)\n","date":"2022.12.20","externalUrl":null,"permalink":"/posts/19/","section":"글","summary":"","title":"이번 주는 이번 달의 몇 주차? 몇 주차는 어떻게 계산하지?","type":"posts"},{"content":"서비스를 개발하기 위한 첫번째 단계인\nVM구성과 Pycharm을 이용한 Django 셋팅 과정 정리\n# [가상환경 셋팅] # 1. 가상환경을 생성할 디렉토리를 생성하고 해당 디렉토리 내부로 진입 # 2. 가상환경 생성 # -\u0026gt; Python 3.5 이후부터는 venv가 파이썬 표준 라이브러리에 내장되어 있기 때문에 따로 설치할 필요가 없으며, Terminal 혹은 cmd 창에서 가상 환경을 생성할 수 있습니다.\npython -m venv test_venv #가상환경 생성 3-1. 가상환경 활성화 # source [가상환경이름]/bin/activate -\u0026gt; 좌측의 괄호안에 가상환경이름이 표시되며, 가상환경이 활성화된것을 확인할 수 있다.\n3-2. 가상환경 비활성화 및 삭제 # deactivate # 가상환경 비활성화(비활성화가 완료되면 좌측에 표시되던 괄호안의 가상환경이름이 없어진다.) sudo rm -rf [가상환경이름] # 가상환경 삭제 [Pycharm Django 셋팅] # 0. 프로젝트파일을 미리 생성하거나 git clone을 진행한다. # 1. Project 생성 # (0) Django : Django로 프로젝트를 셋팅하겠다!\n(1) Location : 연결하고자 하는 프로젝트 디렉토리(작업파일)\n(2) New environment using : python가상환경을 사용할것인지, conda 가상환경을 사용할것인지를 선택한다.\n(이번 예시에서는 위에서 python 가상환경을 생성했고, 거기에 연결할거기때문에 python 가상환경을 선택한다. 만약 conda를 선택한다면, 앞단계에서 굳이 가상환경을 생성해줄 필요가 없이 이단계에서 생성할 수 있는것으로 보인다.)\n(3) Location : 앞전에 생성한 python 가상환경을 연결해준다.\n(4) Base Interpreter : 개발을 진행할 대상 python의 경로를 선택한다. 파이썬의 버전은 이 선택한 경로의 파이썬의 버전을 사용하게 되는것이다. 다만, 앞에서 conda를 선택했다면 파이썬의 버전을 아래 체크박스 영역에서 선택하여 가상환경을 셋팅할 수 있는 장점이 있다.(conda가상환경으로 셋팅하는것이 상당히 편해보인다\u0026hellip;그래서 인지 구글링할때 conda 사용자가 굉장히 많은 것 같았다. 다만, conda의 단점은 무엇이 있을지,,,나중에 한번 알아보도록 하자.)\n2. 확인 # -\u0026gt; 가상환경을 기반으로 기본 파일들이 생성된것을 확인할 수 있다.\n","date":"2022.10.17","externalUrl":null,"permalink":"/posts/12/","section":"글","summary":"","title":"[Python]가상환경 생성 및 Django 셋팅하기","type":"posts"},{"content":"ubuntu의 python 버전을 2.7 -\u0026gt; 3.8으로 변경하기\n1. 현재 파이썬 버전을 확인한다. # python --version # Python 2.7.17 2. 현재 바라보고 있는 파이썬 버전도 확인해본다. # ls -al /usr/bin/python # lrwxrwxrwx 1 root root 9 Apr 16 2018 /usr/bin/python -\u0026gt; python2.7* -\u0026gt; 2.7버전을 바라보고있는것을 확인할 수 있다.\n3. 설치되어있는 파이썬 버전중에서 사용하려고하는 파이썬 버전이 있는지 확인한다. # -\u0026gt; 파이썬은 여러버전을 설치할 수 있고, 그 중 선택된 버전을 사용한다.\nls /usr/bin/ |grep python \u0026#39;\u0026#39;\u0026#39; dh_python2* dh_python3@ python@ python-config@ python2@ python2-config@ python2.7* python2.7-config@ python3@ python3-config@ python3.6* python3.6-config@ python3.6m* python3.6m-config@ python3.8* python3m@ python3m-config@ x86_64-linux-gnu-python-config@ x86_64-linux-gnu-python2.7-config* x86_64-linux-gnu-python3-config@ x86_64-linux-gnu-python3.6-config@ x86_64-linux-gnu-python3.6m-config* x86_64-linux-gnu-python3m-config@ \u0026#39;\u0026#39;\u0026#39; -\u0026gt; 사용하려하는 3.8버전은 없는것을 확인할 수 있다.\n4. 그럼 파이썬 3.8버전을 우선 설치하자! # sudo apt install python3.8 #sudo는 빼도 괜찮음 5. 사용할 파이썬을 등록해주자. # -\u0026gt; 기존의 2.7과 새로 설치한 3.8버전을 일단 모두 등록해둔다.\n(설치되어있는 목록이 있고 이렇게 따로 등록하는 목록이 왜 따로 관리되는지 이유는 잘모르겠다\u0026hellip;.일단 ㄱㄱ)\nupdate-alternatives --install /usr/bin/python python /usr/bin/python2.7 1 # update-alternatives: using /usr/bin/python2.7 to provide /usr/bin/python (python) in auto mode update-alternatives --install /usr/bin/python python /usr/bin/python3.8 2 # update-alternatives: using /usr/bin/python3.8 to provide /usr/bin/python (python) in auto mode 6. 파이썬들이 잘 등록되어있는지 확인해보자. # update-alternatives --config python \u0026#39;\u0026#39;\u0026#39; There are 2 choices for the alternative python (providing /usr/bin/python). Selection Path Priority Status ------------------------------------------------------------ * 0 /usr/bin/python3.8 2 auto mode 1 /usr/bin/python2.7 1 manual mode 2 /usr/bin/python3.8 2 manual mode Press \u0026lt;enter\u0026gt; to keep the current choice[*], or type selection number: ^C \u0026#39;\u0026#39;\u0026#39; -\u0026gt; 잘 등록되어 있네.\n7. 이제 바라보는 파이썬이 3.8로 잘 변경되었는지 확인해보자. # python --version # Python 3.8.0 \u0026ndash;\u0026gt; GOOD!\n==\u0026gt; 만약 등록까지 했지만 버전을 옛날버전으로 바라보고 있다면 아래 내용을 참조해보자.\nRef.\nhttps://codechacha.com/ko/change-python-version/\n","date":"2022.10.14","externalUrl":null,"permalink":"/posts/11/","section":"글","summary":"","title":"[Python 버전 변경]Ubuntu에서 python 버전 변경하기","type":"posts"},{"content":" 1. GPU가 사용 가능한 상태인지 확인 # import os os.environ[\u0026#39;CUDA_DEVICE_ORDER\u0026#39;]=\u0026#34;PCI_BUS_ID\u0026#34; os.environ[\u0026#39;CUDA_VISIBLE_DEVICES\u0026#39;]=\u0026#39;0\u0026#39; device = torch.device(\u0026#39;cuda\u0026#39; if torch.cuda.is_available() else \u0026#39;cpu\u0026#39;) print(\u0026#39;Device:\u0026#39;, device) # print(\u0026#39;Current cuda device:\u0026#39;, torch.cuda.current_device()) print(\u0026#39;Count of using GPUs:\u0026#39;, torch.cuda.device_count()) [Out]\n2. 해당 서버 또는 컨테이너의 shell에서 CUDA버전을 확인 # CUDA버전과 torch관련 라이브러리의 버전이 모두 호환되어야 GPU사용이 \u0026lsquo;True\u0026rsquo;로 출력된다. #서버접속 ssh -p 2022 root@10.70.189.5 #cuda버전 확인(상단에 \u0026#39;CUDA 10.1\u0026#39; 과 같은 형태로 노출) watch -d -n 0.5 nvidia-smi 3. 해당 CUDA버전과 호환되는 torch버전 확인 # torch install link : https://pytorch.org/get-started/previous-versions/ [PyTorch\nAn open source machine learning framework that accelerates the path from research prototyping to production deployment.\npytorch.org](https://pytorch.org/get-started/previous-versions/)\n예시)\n# CUDA 10.1 pip install torch==1.8.1+cu101 torchvision==0.9.1+cu101 torchaudio==0.8.1 -f https://download.pytorch.org/whl/torch_stable.html 4. 터미널 또는 Jupyter notebook 스크립트에서 위 설치 명령문을 실행해서 설치한다. # pip3 list |grep torch # 5. jupyter notebook을 restart한다 # 위 내용이 반영되기 위해 재시작한다. 6. GPU가 사용가능한 상태인지 확인한다. # ","date":"2022.10.11","externalUrl":null,"permalink":"/posts/10/","section":"글","summary":"","title":"[GPU]GPU 셋팅 시 버전 체","type":"posts"},{"content":"","date":"2022.10.11","externalUrl":null,"permalink":"/tags/device/","section":"Tags","summary":"","title":"Device","type":"tags"},{"content":"","date":"2022.10.11","externalUrl":null,"permalink":"/tags/gpu/","section":"Tags","summary":"","title":"Gpu","type":"tags"},{"content":" 1. Background # 들어가기 전에 먼저 Few Shot Learning을 포함하는 개념인 Meta Learning이라는 개념을 먼저 짚고 넘어가고자 한다.\nAI는 궁극적으로 AGI(Artificial General Intelligence, 일반 인공지능/범용 인공지능)로 나아갈 수 있어야하는데, Meta Learning은 AGI로 나아가는데 매우 중요한 디딤돌이 될 개념으로, 사람의 인식과정과 유사하게, \u0026ldquo;구분하는 방법\u0026quot;을 배우는 개념이다.\n차이점을 구분하는 방법을 배운다\n구분 학습방법 전통적 Deep Learning 아르마딜로 사진 1000장 vs 천산갑 사진 1000장 반복 학습 Meta Learning 두 Class의 차이점을 구분하는 방법을 학습 예를들어 천산갑과 아르마딜로를 전혀 보지 못한 어린아이가 위 사진(support set)만으로\nQuery 사진의 동물을 분류한다고 생각해보자.\n→ 아마, 어린 아이는 아주 쉽게 query가 \u0026lsquo;천산갑\u0026rsquo;임을 알 수 있을 것이다.\nHOW? # 사람은 데이터가 충분하지 않더라도 제시된 자료간의 차이점을 이해하는, 즉,**\u0026ldquo;구분하는 법\u0026rdquo;**을 배웠기 때문이다.\n이렇게 사람의 인지과정과 유사하게 \u0026ldquo;구분하는 방법을 배우는 것\u0026quot;을 구현하는 학습 방법을 \u0026lsquo;Meta Learning\u0026rsquo;이라고 한다.\n2. Few-Shot Learning ? # Few Shot Learning은 이름처럼 아주 작은 데이터만으로 학습이 가능하다는 의미인가?\n→ NO! # Few Shot Learning은 Meta Learning의 방법론에 착안한 하나의 학습방법이며, **\u0026ldquo;배우는 법\u0026rdquo;**을 배우기 위해서는 결국 \u0026ldquo;많은\u0026rdquo; 데이터가 필요하다. 다만, 기존의 학습방법과 다른점은 구분하려는 문제(아르마딜로냐 천산갑이냐)의 데이터는 Training set에 없어도 된다는 것이다.\n→ pre-trained model을 활용한 medical image segmentation task에 적용해볼만하다.\n선행 학습이 필요한 Training Set\n1. 기존 Supervised Learning :\nTraining set에 있는 Class에 해당하는 데이터를 예측\n기존 Supervised Learning\n2. Few Shot Learning :\nTraining set에 없는 Class를 맞추는 문제\nFew Shot Learning\nFew Shot Learning을 위해서는 아래 세가지 Data set이 필요하다\n데이터 종류 Desc. Training Set prediction해야할 class와 관련없는 데이터들로 먼저 학습을 진행 → 차이점을 토대로 class를 분류하는 방법을 학습 Support Set 예측할 class와 관련된 1개(one-shot) 또는 n개(few-shot)의 support set을 학습 Query image 예측할 data를 Input Few Shot Learning은 \u0026ldquo;Support Set\u0026quot;의 형태에 따라 Accuracy가 달라진다.\nSupport set의 클래스 개수와 샘플 수(데이터 개수)에 따라k-way(k개의 클래스), n-shot(클래스별 데이터 개수)로 표현하는데,\n클래스가 많아질수록, 클래스별 데이터가 적을수록 정확도는 낮아진다.\n3. Few Shot Learning 학습 방법 # Few Shot Learning의 기본 학습방법은 **\u0026lsquo;유사성\u0026rsquo;**을 학습하는 것이다\n→ 즉, 두 개의 사진이 주어졌을때 각 사진을 잘 분석해서 두 사진이 \u0026ldquo;유사한지, 다른지\u0026quot;를 판단할 수 있다면\nquery image가 주어졌을때 Support set의 사진들과 비교하여 어떤 클래스에 속하는 지 알아낼 수 있다.\n1) Step1 : Feature extraction of training set image\n2) Step2 : Similarity calculation of Features hidden representation Positive pair과 Negative pair에서 각각 한번씩 번갈아가며 학습을 진행\n3) Step3 : support set을 기반으로 query image의 class 예측 step2에서 학습한 패턴을 기반으로 동일하게 support set과 query image의 유사도를 계산하여\nSupport의 class중 하나로 classification할 수 있다.\n","date":"2022.09.29","externalUrl":null,"permalink":"/posts/8/","section":"글","summary":"","title":"[Meta Learning] Few-Shot Learning을 알아보자!","type":"posts"},{"content":"","date":"2022.09.29","externalUrl":null,"permalink":"/tags/classification/","section":"Tags","summary":"","title":"Classification","type":"tags"},{"content":"","date":"2022.09.29","externalUrl":null,"permalink":"/tags/computer-vision/","section":"Tags","summary":"","title":"Computer Vision","type":"tags"},{"content":"","date":"2022.09.29","externalUrl":null,"permalink":"/tags/cv/","section":"Tags","summary":"","title":"Cv","type":"tags"},{"content":"","date":"2022.09.29","externalUrl":null,"permalink":"/tags/dl/","section":"Tags","summary":"","title":"Dl","type":"tags"},{"content":"","date":"2022.09.29","externalUrl":null,"permalink":"/tags/few-shot/","section":"Tags","summary":"","title":"Few-Shot","type":"tags"},{"content":"","date":"2022.09.29","externalUrl":null,"permalink":"/tags/image/","section":"Tags","summary":"","title":"Image","type":"tags"},{"content":"VGG-16 모델은 2014년 Very Deep Convolutional Networks for Large-Scale Image Recognition 논문으로 소개되어,\nImageNet Challenge에서 Top-5 테스트 정확도를 92.7% 달성면서 딥러닝을 활용한 CV 분야에서 큰 성과를 보인 architecture이다.\n오늘은 이렇게 현재 가장 널리 쓰이고 있는 vision모델의 하나인 VGGNet에 대에서 탐구해보자.\n우선 VGG의 뒤에 VGG-16, VGG-19와 같이 숫자가 붙어있는데, 이 부분은 layer의 깊이를 의미하는것이다.\nVGG는 기존 AlexNet에서 적용하지 못했던 deep layer를 적용하여 성능을 크게 개선했다는데 큰 의미가 있다.\n기존 AlexNet 오차율 | 16.4% \u0026ndash;\u0026gt; VGGNet 오차율 7.6%\nVGG-16 Architecture\nVGG중에서 VGG-16을 기준으로, Architecture를 살펴보면,\n흰색박스로 표현된 총 13개의 conv layer가 3개의 Fully-connected layer와 연결된 형태 filter는 3x3으로 적용 붉은색박스로 표현된 2x2의 max-pooling이 적용 ","date":"2022.09.17","externalUrl":null,"permalink":"/posts/4/","section":"글","summary":"","title":"[VGG]VGGNet을 한번 파헤쳐보자!","type":"posts"},{"content":" 0) 의료데이터에서 기존 PCA와 같은 차원 축소 모델을 사용하지 않는 이유 # PCA(주성분 분석)의 경우 선형 차원축소 방식으로, 의료 데이터와 같이 데이터가 교차되며 비선형 구조를 가지는 데이터의 차원을 축소하기에 적합하지 않음. 이에, * 역 그래프 임베딩을 기반으로 한 기본 그래프 구조의 로컬 정보를 캡쳐하는 아래와 같은 모델이 주로 사용됨 * 역 그래프 임베딩 : 기본 그래프 로컬 정보 구조(한마디로, 본래의 형태..?) 를 캡처하는 기법(모델) 1) DDRTree # 구분 Desc 정의 고차원 공간의 데이터 포인트를 저차원의 장점 - 고차원 데이터를 저차원 공간으로 축소하는 차원 축소 기능 제공 - 역 그래프 임베딩을 통해 고유 그래프의 구조를 학습하여 명확한 그래프 구조 복구 가능 - PCA, ICA, Isomap, LLE등 기타 차원 축소 알고리즘보다 고차원의 고유구조에 대한 명시적인 복구가 가능. - 클러스터링 기능을 함께 제공하는 특징 단점 - 10만개의 셀을 초과하는 데이터를 처리할 경우 속도가 매우 느림. Parameter - dim = 축소할 차원 - maxIter = 학습 횟수 - sigma = 데이터간의 차이를 얼마나 민감하게 받아들일지에 대한 파라미터로 보임(클수록 민감) - 데이터간의 차이를 민감하게 받아들여서, 각 좌표간의 차이를 크게 반영하면(파마리터크게)각 포인트 사이의 거리가 멀어짐. - 데이터가 클수록 값도 커져야 함, - ncenter = 크게쓸수록 학습속도가 빨라지는걸봐서,,,weight의 값을 크게 가져가서 빨리 수렴하는것이 아닐까.. 구현결과 +) SimplePPT 2) UMAP # 구분 Desc 논문 UMAP: Uniform Manifold Approximation and Projection for Dimension Reduction github https://github.com/lmcinnes/umap (CPU, scikit-learn, Tensorflow 기반, 다만 매우빠르다고함 → 아직 미확인) - fashion MNIST (70000x784) 데이터에 대해 1분이내 embedding 생성했다고함 정의 1. UMAP 1. topological data분석으로 아이디어와 manifold learning 기술을 기반으로 한 차원축소 알고리즘 1. topological data analysis 1. 위상기반 데이터분석을 통한 접근방식으로 metric에 둔감하고 노이즈에 대한 차원감소 및 견고성을 제공하는 방식으로 동작하는 분석 방식 2. manifold learning 1. PCA와 같은 선형 프레임워크를 비선형 구조에 민감하도록 일반화 하려는 시도를 통해 생성된 학습 기법 * Manifold Learning이란 고차원데이터가 있을 때 고차원 데이터를 데이터 공간에 뿌리면 샘플들을 잘 아우르는 subpsace가 있을 것이라 가정에서 학습을 진행하는 방법. 이렇게 찾은 manifold는 데이터의 차원을 축소시킬 수 있다. 장점 - 시각화 및 분석을 위한 여러 패키지를 함께 제공 - 빠른 임베딩 속도 단점 기타 - 클러스터링 사용할 경우 hdbscan 과 잘 호환됨 구현결과 (+) SimplePPT 3) DDRTree vs UMAP 비교 테스트 결과 # 조건 스코틀랜드 당뇨데이터 sample 1000명 데이터로 비교 구심점의 개수(parameter)는 10개로 동일하게 설정 비교결과 속도 UMAP(6.07s)\u0026raquo; DDRTree(19.39s) 표현형 시각화 비교 (UMAP \u0026gt; DDRTree) 스펙트럼에서 각 표현형을 시각화 했을때, DDRTree의 경우 표현형의 수치별 환자가 분산되어있으나, UMAP의 경우 비교적 밀집되어있음 당뇨여부 시각화 비교 (UMAP \u0026raquo; DDRTree) DDRTree에 비하여 UMAP이 스펙트럼의 특정구간에 당뇨환자를 확연히 밀집하여 표현하고 있음. 추가) SimplePPT → 차원축소모델과 함께 활용 (본래 차원의 그래프 구조 학습을 통해 도출한 주요 포인트를 이어 트리를 시각화..) # 구분 Desc 논문 Ref . Mao et al. (2015), SimplePPT: A simple principal tree algorithm, SIAM International Conference on Data Mining. 1. 내용 1. 실험내용 1. 유방암 및 암관련 표현형 데이터를 활용하여 Gap statistic, Polygonal line, SCMS와 같은 기타 방법론과의 성능 비교 진행 2. 성과 1. 차원축소 시 나선형을 비롯한 여러 형태로 표현되는 표현형 스펙트럼이 기타 알고리즘 대비 가장 분명하게 표현됨 2. 매니폴드 학습의 직관을 반대로 하여 주요 그래프의 표현을 위한 역 그래프 임베딩을 정의. 새로운 표현은 기본 그래프의 길이로 해석될 수 있습니다. 우리는 주 그래프의 길이에 대한 풍부성 제약으로 완화된 양자화 오류를 최소화하여 새로운 주 그래프 모델을 제안합니다. 일반화 경계도 파생됩니다. 3. 데이터로부터 그래프 구조를 학습하기 위해 principaltree 모델이 제시됩니다. 그런 다음 주요 포인트와 트리 구조를 동시에 학습하는 단순 알고리즘을 제안합니다. 이론적이고 실증적인 수렴분석이 제시된다. 4. 다양한 합성 데이터 세트와 고차원 유방암 유전자 발현 데이터 세트에 대한 광범위한 실험이 수행됩니다. 실험 결과는 제안된 주요 트리 방법이 기준선보다 성능이 우수하고 주어진 데이터 세트의 기본 구조를 복구할 수 있음을 보여줍니다. github https://github.com/LouisFaure/simpleppt (GPU를 통한 가속 가능) 정의 1. SimplePPT 1. 역 그래프 임베딩 방식을 통해 기존 구조를 캡쳐하는 기법의 트리 학습 모델 2. 논문 내용 1. 실험내용 1. 유방암 및 암관련 표현형 데이터를 활용하여 Gap statistic, Polygonal line, SCMS와 같은 기타 방법론과의 성능 비교 진행 2. 성과 1. 차원축소 시 나선형을 비롯한 여러 형태로 표현되는 표현형 스펙트럼이 기타 알고리즘 대비 가장 분명하게 표현됨 2. 매니폴드 학습의 직관을 반대로 하여 주요 그래프의 표현을 위한 역 그래프 임베딩을 정의. 새로운 표현은 기본 그래프의 길이로 해석될 수 있습니다. 우리는 주 그래프의 길이에 대한 풍부성 제약으로 완화된 양자화 오류를 최소화하여 새로운 주 그래프 모델을 제안합니다. 일반화 경계도 파생됩니다. 3. 데이터로부터 그래프 구조를 학습하기 위해 principaltree 모델이 제시됩니다. 그런 다음 주요 포인트와 트리 구조를 동시에 학습하는 단순 알고리즘을 제안합니다. 이론적이고 실증적인 수렴분석이 제시된다. 4. 다양한 합성 데이터 세트와 고차원 유방암 유전자 발현 데이터 세트에 대한 광범위한 실험이 수행됩니다. 실험 결과는 제안된 주요 트리 방법이 기준선보다 성능이 우수하고 주어진 데이터 세트의 기본 구조를 복구할 수 있음을 보여줍니다. 장점 - GPU 사용 가능 - DDRTree의 경우 cell이 10만개를 초과할 경우 비실용적으로 느려지는데 반해 비교적 빠르고 궤적을 해석하기 용이함. - 기타 알고리즘 대비, 2차원으로 표현 시 스펙트럼의 구조가 가장 분명하게 표현됨 단점 ","date":"2022.09.13","externalUrl":null,"permalink":"/posts/7/","section":"글","summary":"","title":"[차원축소]차원축소 모델 비교분석_의료","type":"posts"},{"content":"","date":"2022.09.13","externalUrl":null,"permalink":"/tags/pca/","section":"Tags","summary":"","title":"Pca","type":"tags"},{"content":"","date":"2022.09.13","externalUrl":null,"permalink":"/tags/%EC%9D%98%EB%A3%8C/","section":"Tags","summary":"","title":"의료","type":"tags"},{"content":"","date":"2022.09.13","externalUrl":null,"permalink":"/tags/%EC%B0%A8%EC%9B%90%EC%B6%95%EC%86%8C/","section":"Tags","summary":"","title":"차원축소","type":"tags"},{"content":" 1. 목표 # CNN모델 Architecture를 이해한다. 기존 Neural Networks 구조에 Convolutional을 적용하는 이유와 효과에 대해 이해한다. 2. CNN이란 무엇인가? # CNN은 기존 Neural Networks 구조에 Convolution 전처리 작업이 추가되어, 주로 이미지나 영상의 처리에 사용되는 딥러닝 모델이다.\n3. 왜 CNN을 사용하는가? # CNN이 고안된 이유는 vision 데이터 분석에 있어, DNN(Deep Neural Networks)의 문제점을 해결하기 위해 등장하였다 그렇다면, DNN의 문제점은? # A. Input data는 vector(1차원 배열)형태의 데이터를 사용하기때문에, 이미지나 영상데이터의 경우 차원을 축소하여 flatten하는 과정이 필요(완전 연결계층, fully connected layer)\nB. 데이터의 형상을 완전히 무시하기 때문에 지역적 공간적인 정보가 손실\nC. 추상화 과정없이 바로 연산과정에 넘어가기때문에 학습시간과 능률면에서 효율성이 떨어짐.\n예) 3x3의 입력이미지를 vector로 환산한 후 4개의 뉴런을 가진 은닉층을 추가했을때, 총 9x4 = 36개의 가중치를 가지게됨\n즉, 이미지의 모든 영역을 연산하게됨\n그렇다면, CNN의 해결법은? # DNN의 이러한 문제를 해결하기위해 지역적, 공간적 특성을 보존하면서\n가중치도 감소할 수 있도록 개선된 방법이 CNN\n합성곱 연산을 사용할 경우 커널에 해당하는 w0, w1, w2, w3 4개의 가중치만을 가지기 때문에\n훨씬 적은 가중치를 사용하면서 공간적 구조정보를 보존한다는 특징이 있음.\nFilter\n합성곱 연산\n4. CNN의 구조 # CNN Architecture\n5. CNN의 연산과정 # input image (ex. 2차원) -\u0026gt; Image input \u0026amp; Output\nInput Tensor (N, C, H, W) N : 배치의 크기 (몇개인지?) FN : 출력채널개수 (out_channels) OH : feature map의 높이 OW : feature map의 너비 Output Tensor (N, FN, OH, OW) N : 배치의 크기 (몇개인지?) FN : 출력채널개수 (out_channels) OH : feature map의 높이 OW : feature map의 너비 2) Convolution Layer\n[Layer 구성]\ninput image(5 x 5 pixel 단위의 matrix로 구성) → * kernel(filter) 적용(3x3 x 10) → Feature map(convolution result) →\nActivation Function 이때, Feature map + Activation Func를 Convolution Layer 라고함\n[Convolution]\nConvolution (3차원 데이터를 filter를 통해 여러 특성을 띄는 채널(matrix, feature map)으로 나누는 과정)\n단일채널 합성곱 계산의 예 입력값 (input image) d_1 x d_2 필터 (kernel) k_1 x k_2 결과값(Feature map) (d_1 - k_1 + 1) x (d_2 - k_2 + 1) 다수 채널 합성곱 계산의 예(RGB) [Activation Function]\n필터를 통해 얻은 Feature map에 활성함수를 적용하는데, 활성함수의 역할은 Feature map에 특징이 있으면 큰값, 없으면 0에 가까운 정량적인 값으로 구해지게되는데, 이때 이 값을 특징이 \u0026lsquo;있다\u0026rsquo;, \u0026lsquo;없다\u0026rsquo;의 비선형 값으로 바꿔주는 역할을한다. [Parameter]\nStride(보폭) Zero Padding in_channels : 입력 채널 수, 흑백은 주로1, RGB는 주로 3\nout_channels : 출력 채널의 수\n[Pooling Layer (sub sampling)]\nConvolution Layer를 거친 feature map을 인위적으로 줄이는 작업을 pooling이라고 한다. feature map을 통해 이미지의 특징을 얻었지만, 전체 이미지의 특성을 모두 고려할필요는 없기 때문에 처리해야하는 데이터를 줄이기 위해 correlation이 낮은 부분의 영향을 줄이면서 가로 세로 사이즈를 줄이는 과정이다. 효과 데이터의 크기를 줄일 수 있으므로 처리해야할 데이터의 양을 줄일 수 있다, Pooling을 통해 추출한 특징들은 이미지 내에서 위치의 변화에 영향을 덜 받게 되므로 이후 Flatten과정에서 정보 손실을 방지할 수 있다. Poolling 방법 Max Pooling / Average Pooling Parameter (MaxPool2d 기준)\nkernel_size : 커널의 크기\nstride : stride의 크기\npadding : 패딩 크기\nDIlation : 커널 사이의 간격\nceil_mode : output 크기를 계산할때 Ceiling함수 사용 여부 옵션\n이러서 위의 Convolution Layer와 Pooling을 반복하면, 특성추출, 데이터의 축소를 계속해서 반복 수행하게 된다.\n[Flatten(Vectorization)]\n마지막 Pooling Layer의 tensor를 Fully Connected Layer에 input하기 위해 Vector형태로 변환하는 과정을 의미합니다. 앞서, DNN에서 이미지를 1차원으로 변환했을 경우 지역,공간적인 정보들이 소실된다고 했지만, 여기서 마지막 polling data는 convolution과 polling을 반복하면서 입력된 이미지의 특이점 데이터로 구성되어 있기때문에 vector로 변환하여도, 이미지의 정보가 소실되지않는다. [Fully-Connected Layers(Dense Layers)]\n마지막으로 Flatten 을 거친 Vector를 하나 이상의 Fully - connected Layer를 적용 시키고 마지막에 Activation Function을 적용하고 output data를 출력하면됩니다. [추가]\n","date":"2022.09.07","externalUrl":null,"permalink":"/posts/5/","section":"글","summary":"","title":"[CNN]CNN(Convolutional Neural Networks) : 합성곱 신경망을 파헤쳐보자!","type":"posts"},{"content":"강원대병원의 제안으로 AD 관련 논문을 분석하기에 앞서, 의료 관련 더욱이 알츠하이머 관련 domain knowledge가 전무하기 때문에,\n오늘은 간단히 AD와 AD의 주요인자에 대해서 정리하고 논문 분석을 시작하려 한다. 1. 알츠하이머(Alzheimer’s Disease, AD)란 무엇인가? # 알츠하이머(Alzheimer’s Disease 이하 AD라고 한다)란, 기억력의 점진적인 퇴행을 가져오는 뇌의 이상에 서 오는 병이다.\n또한 알츠하이머는 일상생활에 곤란을 겪을 정 도의 심각한 (사고,기억,추론) 지적 기능의 상실을 가져오는\n치매(dementia) 증에 이르게 된다.\n사실 알츠하이머라고 하면 처음 접했던 기억이 어릴 때, \u0026lsquo;투명인간 최장수\u0026rsquo;라는 드라마를 보면서,\n어떤 병보다도 본인과 가족들에게 잔인한 질병이 아닌가 하는 생각이 들었던 기억이 있다.\n무튼, 이런 무서운 병이 왜 생기는지 한번 알아보자.\n2. 알츠하이머(AD)의 주요 인자는 무엇이 있을까? # 1. 베타 아밀로이드(Aβ) # 알츠하이머병에 결정적으로 관여하는 36~43개의 아미노산 펩타이드 펩타이드라는 것이 \u0026lsquo;사슬\u0026rsquo;이라는 의미인데, 결국 아미노산으로 이루어진 사슬, 덩어리 정도로 이해할 수 있지 않을까 싶다. AD가 진행됨에 따라 Aβ가 축적되어 아밀로이드 플라그가 형성되고, 이것이 뇌의 신경세포 퇴행과 직접적인 관련이 있는 것으로 지금까지 연구되었다. 다만, 지금까지 Aβ와 AD의 관계를 기반으로 진행된 대규모 임상시험이 잇따라 실패하면서, Aβ와 AD와의 관련성에 의문이 제기되고 있고, 최근 연구에서는 Aβ가 질병 유발인자 이기는 하지만 신경세포의 퇴행과 직접적인 관련이 적다는 연구도 발표되고 있다고 한다. 2. 타우(tau)단백질 # 최근 연구에서는 AD의 주범이 뇌신경세포 표면에 침착된 베타 아밀로이드가 아니라 타우(tau) 단백질일 가능성이 높다는 연구가 있었다. 병리적인 타우-단백질의 엉킴(tangles)을 영상화(PET를 통해)하면 뇌가 위축될 위치를 1년 이상 앞서 예측 가능하다는 연구가 있었음. 최근 22년 4월 발표된 Nature Communications 논문(코넬 의대)에서 \u0026ldquo;변형 타우 → NF-kB경로 자극 → 소교세포 과잉 활성화 → 염증촉진 → 소교세포의 변형타우 포식 둔화 → 뉴련 손상 확산\u0026quot;과 같이 타우 단백질이 신경세포를 손상시키는 과정에 대해서 밝혀냄. 결국, Aβ는 AD 문제를 해결하지 못했고, 최근 주목받고 있는 tau단백질로 이제 연구의 흐름이 넘어가고 있지 않나 싶다.\n3. 알츠하이머(AD)는 어떻게 진단할 수 있을까? # 1. 영상 바이오 마커 # FDG-PET 영상\n\bA. 영상 바이오 마커의 종류와 특징\namyloid-PET(positron emission tomography) 뇌 영상 검사법 치매 병리를 반영한 바이오마커인 베타 아밀로이드(Aβ)를 타깃으로 하는 뇌영상법이다. 한계점 Amyloid-PET는 인지기능이 정상인 노인에서도 아밀로이드 플라그(amyloid plaque)가 보일 수 있는 한계점이 있음 아밀로이드 플라그 주위에 존재하는 뇌신경들이 사멸하는 과정과 인지기능의 저하까지는 10-15년의 간극 기간 때문 FDG-PET 검사법 포도당 유사체(F-18 FDG)를 이용해 뇌의 당대사 변화를 영상화하여 치매 진단에 이용하는 방법. 손상된 신경세포는 포도당 대사 기능이 저하되기 때문에 정상인에 비해 치매환자의 뇌에서 포도당대사 정도가 떨어지는 것을 이용한 방법이다. tau 검사법 최근에는 치매 병리의 또 다른 바이오마커인 타우(tau) 단백질을 타깃으로 이미징 하여 진단에 활용하고자 tau-PET도 개발 중에 있으나 임상에 적용되기에는 보다 많은 연구가 진행되어야 할 것 2. 뇌척수액 바이오마커 (Cerebrospinal fluid, CSF) # A. 측정 원리\n치매환자 뇌에서 신경퇴화가 진행됨에 따라 베타 아밀로이드(Aβ) 축적으로 아밀로이드 플라그(amyloid plaque)가 형성되지만 한편 뇌척수액(Cerebrospinal fluid, CSF)에서는 베타 아밀로이드 농도 감소가 관찰 또 다른 병변인 신경섬유다발(Neurofibrillary tangle, NFT)과 관련 있는 타우(tau) 단백질은 치매환자의 뇌척수액에서 전체 타우 단백질과 과인산화된 타우단백질 모두 농도가 증가되는 것으로 보고됨 이는 치매환자에서 뇌신경세포 사멸에 따라 세포 밖으로 흘러나온 타우 단백질이 뇌척수액에서 증가하기 때문인 것으로 보고 있다. B. 검사법\n위 원리를 활용한 검사법이 각각 CSF-Aβ 및 CSF-tau/p-tau 양을 측정하는 방법 현재 정상인과 치매환자 뇌척수액에서 베타 아밀로이드 및 타우 단백질 평균 농도까지 알려져 있어 이를 이용한 치매 진단 마커로 개발이 가장 활발 C. 한계점\n뇌척수액(CSF)을 이용한 치매진단은 환자 척추를 천공하는 요추천자(lumbar puncture)를 통해 뇌척수액을 얻어야 하는 침습적인(invasive) 방법이라 환자나 보호자에게 거부감을 주는 문제점 3. 혈액 바이오 마커(연구 진행 중) # A. 특징\n가족력이 있는 치매환자(familial AD) 혈액에서 베타 아밀로이드(Aβ) 농도가 증가 혈액(plasma, serum)에서 베타아밀로이드 농도를 측정하는 진단법이 제안되었으나 여러 연구결과 치매 조기진단에 사용하기에는 재현성(reproducibility)이나 특이성(specificity)에 문제가 있는 것으로 판단 치매환자 뇌에서는 미세 교세포(microglia)가 활성화되고 염증성 사이토카인(cytokine) 분비 증가로 신경세포 사멸이 유도된다고 한다. 이러한 치매 병리에 기반하여 치매환자 혈장에 존재하는 18가지의 생물학적 지표(chemokines, cytokines, growth factors, binding proteins)가 정상에 비하여 치매 및 MCI 환자에서 차이를 보인다고 하였지만 재현실험에서는 실패하였다 최근, MCI나 치매로 발병되기 전 단계에 있는 노인(〉70세)의 혈청에서 10종의 인지질(phospholipids) 양이 90% 정확도를 보이며 감소한다는 것을 대사체(metabolomics) 분석을 통하여 발견 본 연구자들은 인지질 10종 중에서 재검증 연구를 통하여 보다 현저한 차이를 보이는 인지질을 선정하고 ApoE 유전자 타입 등과 조합하여 임상진단에 이용한다면 실제 진단에서 이용 가능성을 제시한 상태임. 이번 포스팅에서는 알츠하이머라는 질병과 관련된 주요 인자, 측정방법에 대해서 정리해보았다.\n다음에는 이번에 조사한 알츠하이머 관련 내용들을 참고,\nML을 활용해서 알츠하이머 연구에 유용한 insight를 얻은 연구들에 대해서 리뷰해보도록 하겠다.\n","date":"2022.09.06","externalUrl":null,"permalink":"/posts/2/","section":"글","summary":"","title":"[Background knowledge] 알츠하이머란?","type":"posts"},{"content":"","date":"2022.09.06","externalUrl":null,"permalink":"/tags/ad/","section":"Tags","summary":"","title":"Ad","type":"tags"},{"content":"","date":"2022.09.06","externalUrl":null,"permalink":"/tags/alzheimer/","section":"Tags","summary":"","title":"Alzheimer","type":"tags"},{"content":"","date":"2022.09.06","externalUrl":null,"permalink":"/tags/medical/","section":"Tags","summary":"","title":"Medical","type":"tags"},{"content":"","date":"2022.09.06","externalUrl":null,"permalink":"/tags/%EC%95%8C%EC%B8%A0%ED%95%98%EC%9D%B4%EB%A8%B8/","section":"Tags","summary":"","title":"알츠하이머","type":"tags"},{"content":"","date":"2022.09.06","externalUrl":null,"permalink":"/tags/%EC%9D%98%EB%A3%8Cai/","section":"Tags","summary":"","title":"의료ai","type":"tags"},{"content":"고객의 문제를 직접 들여다보고, 그 상황에 맞는 에이전트와 모델을 만들어 현장에 배포합니다. 아래는 지금까지 만들어 온 것들입니다.\n사내 AX 경진대회 대상 2026.09 Knowledge Layer 표준화 플랫폼 기업 폐쇄망에 흩어진 레거시 리소스(API · DB · 비정형 문서)를 에이전트가 이해할 수 있는 Knowledge Layer로 통합하고 관리하는 플랫폼. Veil-PII-Ko-Lite 공개 2026.09 경량 한국어 개인정보 탐지 모델 폐쇄망 CPU 환경용 110M 모델. 1.4B 비교 모델보다 넓은 32개 라벨을 KDPII F1 0.934로 탐지. 개발 리포트 · Hugging Face 보험 설계 멀티 에이전트 2026.03 – 진행 중 금융 폐쇄망 · Azure Private Endpoint Supervisor가 6종 서브 에이전트를 ReAct로 호출하고 50종 API를 Tool로 다루는 구조를 설계. Private VNet 인프라, Container Apps 오토스케일, Durable Functions 인덱싱까지 담당. 보험 도메인 임베딩 Fine-Tuning 2026 E5 (검색) · KURE (색인) · LoRA Tool·Agent 검색용 E5는 도메인 NDCG@10 0.773 (+14.3pt), 약관 색인용 KURE는 7.5K 토큰 긴 문서 R@1 77.7 (OpenAI 대비 +59.5pt). E5 모델 · KURE 모델 FDE 딜리버리 에이전트 (Harness) 2025 – 내부 멀티 에이전트 · 리드 고객 요구사항(PRD)을 받아 도메인 에이전트를 해석 → 설계 → 구현 → 검증까지 자동화하는 Harness 전체 구조를 설계하고 개발을 리드. 보험 세일즈 어시스턴트 2025.06 – 2025.12 Plan \u0026amp; Execute · Multi-hop 검색 근거가 부족하면 질의 재작성 → 재계획 → 재실행을 반복하는 Multi-hop 루프. 약관 300GB를 Parent-Child로 청킹하고 Durable Functions로 실시간 인덱싱. ERP 업무비서 에이전트 2023.10 – 2025.04 1,000개 기업이 쓰는 B2B SaaS 400개 Tool 중 하나를 고르는 2단계 Hybrid Tool Retrieval로 실운영 정확도 93%+. CPU에서 도는 0.4B RoBERTa 임베딩을 LoRA · SimCSE로 도메인 튜닝하고, 64대 서버에 10% → 전체로 점진 배포. ML 모델 개발 이전 작업 Tabular · NLP 법인세 신고서식 예측(FT-Transformer, 정확도 87.3%), 거래 데이터 기반 계정 추천 · 기업 분류 모델, 이상 경비 청구 탐지(GloVe · BM25 앙상블). Stack # Agent LangGraph · Multi-Agent · ReAct · Plan\u0026amp;Execute · RAG Retrieval E5 · KURE · RoBERTa · LoRA · Azure AI Search · FAISS · Re-ranking Backend Python · FastAPI · PostgreSQL · Cosmos DB · Redis · PyTorch Cloud Azure Container Apps · Durable Functions · Key Vault · Docker · GitHub Actions · Langfuse Contact # zzang891014@gmail.com · GitHub · Hugging Face\n","externalUrl":null,"permalink":"/about/","section":"Kim Won Tae","summary":"","title":"소개","type":"page"},{"content":"직접 설계하고 만들어서 실제 환경에 올린 작업들입니다. 고객사 정보는 적지 않았고, 수치는 각 프로젝트의 평가셋 기준입니다.\n공개 모델 # Veil-PII-Ko-Lite — 경량 한국어 개인정보 탐지 모델 2026 # 폐쇄망 CPU 환경에서 쓸 수 있는 한국어 PII 탐지 모델이 없어 직접 만들었습니다. KoELECTRA 백본에 BIES 태깅과 Viterbi 디코딩, 포맷 규칙 기반 합성 데이터를 더했습니다.\n110M 파라미터 · INT8 143MB로 1.4B 비교 모델보다 넓은 32개 라벨 지원 KDPII test F1 0.934 (1.4B 비교 모델 0.453) 개발 리포트 · Hugging Face · Docker Hub 보험 도메인 검색 임베딩 (E5 · LoRA) # 에이전트 안에서 매우 자주 호출되는 Tool·Agent 검색용 모델. CPU에서 가볍게 돌도록 118M · 384dim 모델을 골라 LoRA(0.44%)로 보험 도메인에 맞췄습니다.\n도메인 평가셋(104 query) NDCG@10 0.773 (base 0.630, +14.3pt) · R@1 0.596 (+21.1pt) OpenAI text-embedding-3-large보다 도메인 NDCG가 높고(0.773 vs 0.634), 차원은 1/8 Hugging Face 보험 약관 색인 임베딩 (KURE · LoRA) # 긴 약관 문서를 페이지 단위로 색인하려고 8,192 토큰을 받는 KURE-v1을 보험 문서 42,626페이지로 튜닝했습니다. 256~7,500 토큰 멀티 레졸루션 커리큘럼과 hard-negative mining을 썼습니다.\n긴 문서(7.5K 토큰) R@1 77.7 — base 55.6, OpenAI 18.2 대비 +59.5pt 일반 한국어 성능(KorSTS) 변화 −0.07pt로 망각 없이 유지 Hugging Face 현장 프로젝트 # 금융 폐쇄망 · 보험 설계 멀티 에이전트 2026 – 진행 중 # 설계사의 가입 설계 업무를 돕는 멀티 에이전트. Supervisor가 6종 서브 에이전트를 ReAct 방식으로 호출하고 50종 API를 Tool로 다룹니다.\n역할: Supervisor 라우팅 아키텍처 설계, Private VNet · Private Endpoint 인프라, Container Apps 오토스케일 운영, Durable Functions 인덱싱, 임베딩 Fine-Tuning 스택: LangGraph · FastAPI · Azure AI Search · Cosmos DB · PostgreSQL · Container Apps · Langfuse 금융 폐쇄망 · 보험 세일즈 어시스턴트 2025 # 복잡한 보험 질의를 Plan\u0026amp;Execute와 Multi-hop 검색으로 단계적으로 푸는 에이전트. 근거가 부족하면 질의 재작성 → 재계획 → 재실행을 반복하고, Rerank로 핵심 근거를 골라 답합니다.\n약관 300GB를 Parent-Child로 청킹해 벡터 검색 구성, Durable Functions 실시간 인덱싱 스택: LangGraph · Azure AI Search · Container Apps (Private Endpoint) · Arize Phoenix FDE 딜리버리 에이전트 (Harness) 리드 # 고객 요구사항(PRD)을 받아 도메인 에이전트를 해석 → 설계 → 구현 → 검증까지 자동화하는 내부 멀티 에이전트. 전체 구조 설계와 내부 에이전트 개발을 리드했고, 실제 보험 도메인 딜리버리에 쓰였습니다.\nERP 업무비서 에이전트 ~ 2025.04 # 1,000개 기업이 쓰는 B2B SaaS 업무비서. 자연어 요청을 의도 분류해 회계·세무·인사·그룹웨어 기능으로 라우팅하고, 사내 문서 RAG QA를 제공했습니다.\n400개 Tool 중 하나를 고르는 2단계 Hybrid Tool Retrieval(임베딩 → LLM)로 실운영 정확도 93%+ CPU에서 도는 0.4B RoBERTa 임베딩을 LoRA로 회계·세법 도메인에 튜닝 64대 운영 서버에 10% → 20% → 50% → 80% → 전체 순으로 점진 배포 그 밖의 ML # 법인세 신고서식 예측 — FT-Transformer 기반 Tabular 모델, 정확도 87.3% 기업 거래 데이터 기반 계정과목 추천 · 기업 분류 모델 이상 경비 청구 탐지 — GloVe · BM25 앙상블, NLP + Clustering ","externalUrl":null,"permalink":"/projects/","section":"Kim Won Tae","summary":"","title":"프로젝트","type":"page"}]