<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>yooni1231__.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 25 Nov 2025 05:14:44 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>yooni1231__.log</title>
            <url>https://velog.velcdn.com/images/yooni1231__/profile/e5e569d8-0f8a-49b3-8cfa-d9b4ef9e892f/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. yooni1231__.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/yooni1231__" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Java Coding Test - Input/Output]]></title>
            <link>https://velog.io/@yooni1231__/Java-Coding-Test-InputOutput</link>
            <guid>https://velog.io/@yooni1231__/Java-Coding-Test-InputOutput</guid>
            <pubDate>Tue, 25 Nov 2025 05:14:44 GMT</pubDate>
            <description><![CDATA[<h4 id="1-scanner-말고-bufferedreader를-써야하는-이유">1. Scanner 말고 BufferedReader를 써야하는 이유</h4>
<p>성능 차이 : BUfferedReader - 데이터를 한 덩어리로 읽어서 버퍼 (8KB)에 저장
           Scanner - 매번 작은 단위로 읽고 파싱하는 과정이 많음 </p>
<pre><code>       ```</code></pre><p>BufferedReader br = new BufferedReadre (new InputScannerReader(System.in));
int n = Integer.parseInt(br.readline());</p>
<h4 id="2-알고리즘-문제의-기본-구조">2. 알고리즘 문제의 기본 구조</h4>
<p>1) 첫 줄에 n 입력
2) 둘째 줄에 n개의 숫자 입력
3) 반복문으로 처리 
4) 결과 출력</p>
<pre><code>int n = Integer.parserInt(br.readLine());
StringTokenizere st = new Stringtokenizer(br.readline());
for ( int i=0; i&lt;n; i++){
arr[i]=Integer.parseInt(st.nextToken());
}</code></pre><h4 id="21-입력-패턴">2.1 입력 패턴</h4>
<p>2.1-1 숫자 하나</p>
<pre><code>int n= Integer.parseInt(br.readline());
</code></pre><p>2.1-2 한 줄에 여러 숫자
입력 : 3 10 7</p>
<pre><code>StringTokenizer st = new StringTokenizere(br.readLine());
int a = Integer.parseInt(st.nextToken());
int b = Integer.parseInt(st.nextToken());
int c = Integer.parseInt(st.nextToken());
</code></pre><p>2.1-3 배열 입력
첫 줄 : 5
둘째 줄: 1 2 3 4 5</p>
<pre><code>int n = new StringTokenizer(br.readLine());
int[] arr = new int[n];
StringTokenizer st = new StringTokenizer(br.readLine())
for (int i=0; i&lt;n; i++){
arr[i]=Integer.parseInt(st.nextoken());
} </code></pre><p>2.1-4 문자열 입력 </p>
<pre><code>String s= br.readLine();</code></pre><h4 id="22-출력-패턴">2.2 출력 패턴</h4>
<p>2.2-1 단일 결과 출력</p>
<pre><code>System.out.println(answer);
</code></pre><p>2.2-2 여러 줄 출력</p>
<pre><code>StringBuilder sb = new StringBuilder();
for (int i=0; i&lt;n; i++){
sb.append(arr[i].append(&#39;/n&#39;);
}
System.out.print(sb);</code></pre><h4 id="23-입출력-기본-템플릿">2.3 입출력 기본 템플릿</h4>
<pre><code>import java.io.*;
import java.util.*;

public class Main{
public static void main(String[] args){
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringBuilder sb = new StringBuilder(); // 여러줄의 출력을 효율적으로 만듬 

//입력 
int n = Integer.parseInt(br.readLine()); //한줄을 읽어 문자열을 정수로 바꿈 
StringTokenizer st = new StringTokenizer(br.readLine()); //공백을 기준으로 토큰 분리 
int[] arr = new int[n];
for(int i=0; i&lt;n; i++){
arr[i]=Integer.parseInt(st.nextToken());
}

//출력
System.out.print(sb);
}}</code></pre><h4 id="24-전체-예제">2.4 전체 예제</h4>
<p>백준 A+B 문제</p>
<pre><code>import java.io.*;
import java.utils.*;

public class Main{
public static void main(String[] args) throws IOException{

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int a = Integer.parseInt(st.nextToken());
int b = Integer.parseInt(st.nextToken());

System.out.println(A+B);
}
}
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[숏폼 비디오 자동 생성 프로그램 사용 모델 ]]></title>
            <link>https://velog.io/@yooni1231__/%EC%88%8F%ED%8F%BC-%EB%B9%84%EB%94%94%EC%98%A4-%EC%9E%90%EB%8F%99-%EC%83%9D%EC%84%B1-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EC%82%AC%EC%9A%A9-%EB%AA%A8%EB%8D%B8</link>
            <guid>https://velog.io/@yooni1231__/%EC%88%8F%ED%8F%BC-%EB%B9%84%EB%94%94%EC%98%A4-%EC%9E%90%EB%8F%99-%EC%83%9D%EC%84%B1-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EC%82%AC%EC%9A%A9-%EB%AA%A8%EB%8D%B8</guid>
            <pubDate>Thu, 20 Nov 2025 00:55:48 GMT</pubDate>
            <description><![CDATA[<p>** 본 포스팅은 2024년 상반기 진행했던 프로젝트에서 사용했던 컴퓨터 비전 파이프라인과 DeepOCSort 알고리즘 모델에 대한 설명을 담은 글입니다. 이 시스템은 사용자가 업로드한 원본 동영상에서 특정 인물을 추적하고, 해당 인물을 중심으로 9:16 숏폼 비디오를 자동 생성하는 것을 목표로 합니다. **</p>
<h3 id="핵심-문제-및-computer-vision-파이프라인-자동화">핵심 문제 및 Computer Vision 파이프라인 자동화</h3>
<p><strong>1.해결하고자 했던 문제</strong></p>
<p>기존 수동 편집 방식에서는 동영상 전체를 보며 특정 인물을 지속적으로 중앙에 배치하고, 화면 비율에 맞게 크롭 영역을 수동으로 조정하는 과정이 시간 소모적인 문제였습니다. 우리 프로젝트는 이 과정을 인물 탐지 -&gt; 인물 추적 -&gt; 스마트 크롭의 3단계 computer vision  파이프라인으로 자동화하여 해결했습니다. </p>
<p><strong>2. 파이프라인 구성</strong></p>
<ol>
<li>인물 탐지 : 비디오 프레임마다 존재하는 모든 인물의 경계 상자의 좌표를 추출합니다. ( YOLOv5 사용)</li>
<li>인물 추적 : 사용자가 선택한 특정 인물에 고유 ID를 부여하고, 프레임이 바뀌어도 해당 ID를 유지하며 추적합니다.</li>
<li>스마트 크롭: 추적된 인물의 위치를 기반으로, 최종 숏폼 비디오의 화면 중앙에 인물이 자연스럽게 위치하도록 ROI(Region of Interest)를 동적으로 계산합니다.</li>
</ol>
<p><strong>3. DeepOCSort:인물 재식별 추적 알고리즘 구현</strong>
단순한 객체 탐지나 기본적인 추적 알고리즘은 인물이 프레임 밖으로 잠시 나갔다 들어오거나 다른 사람과 겹칠 때 ID가 바뀌는 한계가 있습니다. 이를 해결하기 위해 DeepOCSort 알고리즘을 최종 구현에 도입했습니다. </p>
<ol>
<li>구성요소
DeepOCSort (Deep Simple Online and Realtime Tracking)</li>
</ol>
<ul>
<li>칼만 필터: 객체 다음의 위치를 예측하는데 사용되어 움직임 기반의 매칭 기초를 제공</li>
<li>Re-Id 네트워크: 딥러닝 모델을 사용하여 각 인물의 외관적 특징 벡터를 추출합니다. 이 벡트는 인물의 고유한 지문 역할을 하여, 인물이 가려지거나 잠시 사라져도 동일 인물임을 재식별하는데 결정적인 역할을 합니다. </li>
</ul>
<ol start="2">
<li>OSNET(Omni-Scle Network)</li>
</ol>
<ul>
<li>DeepSORT에서 외관 특징 추출기로 사용되는 강력한 Re-ID 모델입니다. 다중 스케일(Omni-Scale) 특징을 효과적으로 포착하여, 인물이 멀리 있거나(작게 보일 때) 가까이 있을 때(크게 보일 때) 모두 특징 벡터를 생성합니다.</li>
<li>DeepOCSort에서는 DeepSORT의 프레임워크 내에서 OSNET이 특징 추출자 역할을 수행함으로써 추적의 정확도(Identity Preservation)를 극대화합니다.</li>
</ul>
<p><strong>4. 스마트 크롭 알고리즘 설계 및 구현</strong>
DeepOCSort가 반환하는 특정 인물의 경계 상자 꼭짓점 좌표를 기반으로 , 최종 숏폼 비디오를 위한 최적의 크롭 영역을 동적으로 계산했습니다. </p>
<ul>
<li><p>동적 ROI 계산 로직 
추적 인물이 대상이 9:16화면의 중앙에 위치하고 화면에서 자연스러운 여백을 확보하는 것입니다. 
중심 좌표 계산: $$C_x = \frac{x_{min} + x_{max}}{2}, \quad C_y = \frac{y_{min} + y_{max}}{2}$$크롭 영역의 크기 결정:원본 동영상의 화면 너비 $W_o$, 높이 $H_o$.최종 숏폼의 화면 비율은 $9:16$입니다.</p>
</li>
<li><p>최종 RoI 좌표 계산:
크롭 영역이 중심 좌표 $(C_x, C_y)$를 중심으로 하도록 최종 RoI의 네 모서리 좌표 $(\mathbf{X}{start}, \mathbf{Y}{start}, \mathbf{X}{end}, \mathbf{Y}{end})$를 계산합니다. 여백 계산 로직을 적용하여 화면을 벗어나지 않게 조정합니다 (예: $X_{start}$가 0보다 작아지면 $X_{start}=0$으로 설정하고, $X_{end}$가 $W_o$를 초과하지 않도록 제한).</p>
</li>
<li><p>스무딩 필터 적용 ( 떨림 방지 ):
추적 좌표는 프레임마다 미세하게 흔들릴 수 있으며, 이로 인해 최종 크롭 화면이 &#39;떨리는(Jittering)&#39; 현상이 발생합니다.
이를 방지하기 위해 RoI의 중심 좌표 $(C_x, C_y)$에 <strong>지수 이동 평균(Exponential Moving Average, EMA)</strong> 스무딩 필터를 적용했습니다.</p>
</li>
</ul>
<p>$$\text{Smoothed } C_x^t = \alpha \times C_x^t + (1 - \alpha) \times \text{Smoothed } C_x^{t-1}$$</p>
<p>여기서 $\alpha$는 스무딩 계수로, $\alpha$ 값이 작을수록 움직임은 부드러워지지만 반응 속도는 느려집니다. 최적의 $\alpha$ 값을 튜닝하여 부드러운 화면 이동과 즉각적인 인물 추적 사이의 균형을 맞췄습니다.</p>
<p><strong>5. 백엔드 파이프라인 구현 ( Flask &amp; FFMPEG )</strong>
전체 AI 모델 파이프라인의 구동과 최종 비디오 렌더링은 Flask 기반의 백엔드 시스템을 통해 자동화되었습니다.</p>
<ul>
<li>Flask API 엔드포인트 개발
사용자로부터 원본 영상 파일을 업로드받는 API 엔드포인트를 개발했습니다.</li>
</ul>
<p>업로드된 영상은 <strong>백엔드 서버에서 인물 탐지 → DeepOCSort 추적 → 스마트 크롭 계산 과정</strong>을 거칩니다.</p>
<p>AI 모델 파이프라인의 최종 결과는 각 프레임별 최적의 크롭 영역 좌표 (RoI) 데이터입니다.</p>
<ul>
<li>FFMPEG을 활용한 자동 인코딩 및 렌더링
AI 모델이 계산한 좌표값을 기반으로 실제로 숏폼 비디오 파일을 생성하는 데 FFMPEG 라이브러리를 활용했습니다.</li>
</ul>
<p>데이터 전달: Flask API는 AI 모델에서 반환된 프레임별 RoI 좌표 리스트를 FFMPEG 명령에 전달할 수 있는 형태로 가공합니다.</p>
<p>FFMPEG 필터 적용: FFMPEG의 crop 필터 기능을 사용하되, 필터의 인수를 프레임별로 동적으로 변경할 수 있도록 구현했습니다. 이는 좌표 리스트를 기반으로 타임스탬프에 따라 크롭 영역이 부드럽게 전환되도록 명령어를 구성하는 핵심입니다.</p>
<p>자동 인코딩: FFMPEG은 이 명령을 받아 원본 동영상을 자동으로 읽어 들이고, 추적된 인물을 중앙에 배치하는 동적 크롭 및 9:16 비율 조정을 수행한 후, 최종 숏폼 비디오로 인코딩 및 렌더링합니다.</p>
<p>이 파이프라인 구축을 통해 프론트엔드에서의 영상 업로드 요청 하나로 AI 분석부터 최종 결과물 생성까지의 전 과정이 자동화되었습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[라즈베리파이5로 카메라 2대 + 듀얼 마이크로 실시간 대화하기 (1) ]]></title>
            <link>https://velog.io/@yooni1231__/%EB%9D%BC%EC%A6%88%EB%B2%A0%EB%A6%AC%ED%8C%8C%EC%9D%B45%EB%A1%9C-%EC%B9%B4%EB%A9%94%EB%9D%BC-2%EB%8C%80-%EB%93%80%EC%96%BC-%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EB%8C%80%ED%99%94%ED%95%98%EA%B8%B0-1</link>
            <guid>https://velog.io/@yooni1231__/%EB%9D%BC%EC%A6%88%EB%B2%A0%EB%A6%AC%ED%8C%8C%EC%9D%B45%EB%A1%9C-%EC%B9%B4%EB%A9%94%EB%9D%BC-2%EB%8C%80-%EB%93%80%EC%96%BC-%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EB%8C%80%ED%99%94%ED%95%98%EA%B8%B0-1</guid>
            <pubDate>Fri, 27 Jun 2025 06:27:35 GMT</pubDate>
            <description><![CDATA[<p>앞서 개발한 소프트웨어를 적용시킬 하드웨어를 설정할 단계입니다. </p>
<p>Rasberry 5를 기반으로 다음과 같은 구조의 실시간 AI 대화 시스템을 구축해보겠습니다. </p>
<p>필요한 물품은 다음과 같습니다. </p>
<br>
<br>
<br>
<br>

<br>

 <h2>🧩 사용한 하드웨어 구성</h2>
  <table border="1" cellpadding="8">
    <thead>
      <tr><th>구성 요소</th><th>모델명</th><th>기능</th></tr>
    </thead>
    <tbody>
      <tr><td>Raspberry Pi 5</td><td>8GB</td><td>메인 컨트롤 보드</td></tr>
      <tr><td>Camera Module 3</td><td>RGB / NoIR</td><td> 사용자가 보고 있는 그림 인식 </td></tr>
      <tr><td>ReSpeaker 2-Mics Pi HAT</td><td>Seeed</td><td>마이크 2개 내장, 오디오 입출력</td></tr>
      <tr><td>IR LED 조광기</td><td>850nm</td><td> 사용자의 동공 움직임 감지 </td></tr>
      <tr><td>스피커</td><td>유선 or USB</td><td>GPT 응답 음성 출력</td></tr>
    </tbody>
  </table>

  <br>
  <br>
<br>
<br>
<strong> 주의: 모든 부품은 연결하기 전 라즈베리파이의 전원을 꼭 off 해주세요.
  <UL> 부품 분해 후 봉투에 잘 넣어 보관해주세요. </strong> 
  </ul>
  <br>
  <br>

   <h2> 📌  라즈베리파이5 </h2>

<img src="https://velog.velcdn.com/images/yooni1231__/post/2f9312fa-5e73-4104-ad34-18a34b06d5c8/image.png">



   <h2> 📷 카메라 </h2>
  <ul>
    <li><strong>CAMERA 0</strong> 포트 → 일반 RGB 카메라</li>
    <li><strong>CAMERA 1</strong> 포트 → NoIR 적외선 카메라</li>
    <li>Pi 5는 22핀 CSI 전용 케이블 사용 (15핀은 호환되지 않음)</li>

  <br>
  <strong> 주의: </strong> <p>
  <li> Pi 5는 고밀도 22핀 csi 포트를 사용하므로 동봉된 전용 케이블을 사용해야 합니다. 기존 1핀 케이블은 호환되지않습니다. 
    <li> 포트를 양 끝으로 조심스럽게 들어올린 후 꼽아주시면 됩니다. 

  <li> 케이블 방향을 조심해주세요. 검은색 부분이 이더넷 연결 쪽을 향해야 합니다. 


 <img src ="https://velog.velcdn.com/images/yooni1231__/post/aa45f649-5959-4295-870f-4f6f69fc1f8d/image.png">

 <br>
 <br> 
 <br>
    <h3>   👁️ 적외선 조광과 NoIR 카메라의 역할
    </h3> <h4> NoIR 카메라 란? </h4>
    <br>
    <li> 적외선 차단 필터가 제거된 라즈베리파이용 카메라 모듈로,
사람 눈에는 보이지 않는 850nm~940nm 대역의 IR 조명을 받아들일 수 있음 
    <li> 어두운 환경에서도 선명한 눈 주변 이미지를 촬영할 수 있음 


<img src="https://velog.velcdn.com/images/yooni1231__/post/2020f5a1-3265-43ae-9b28-cb13ccddb2d2/image.png">

 <br>
<br>
  <br>
  <br>
  </ul>

  <br>
  <br>
<br>


  <h2>🎤 오디오 입출력</h2>
  <p>HAT에 내장된 마이크 2개로 음성 입력, 3.5mm 포트를 통해 스피커 연결 가능.
  USB 스피커는 선택사항이며 필수는 아닙니다.</p>
  <ul> <h3> 오디오 보드 연결 및 작동</h3> 
    Respeaker 2-Mics Pi HAT 은 GPIO에 바로 장착합니다.
    마이크 2개가 내장되어 있어 별도 입력 장치가 없이 사용자의 음성을 수신할 수 있습니다. 

  <img src="https://velog.velcdn.com/images/yooni1231__/post/975f448d-8c72-4a62-88fb-cd507edc838c/image.png">

<pre><code>듀얼 마이크 보드가 안쪽으로 오게 끼우시면 됩니다. </code></pre>  </ul>
<br>
    <br>
    <br>
<br>



<h2>🧩 완성</h2> 
<img src="https://velog.velcdn.com/images/yooni1231__/post/f2af2ec6-9992-4ea8-af7e-84288e899a28/image.png">
]]></description>
        </item>
        <item>
            <title><![CDATA[Implementing Book Info Crawling from Yes24 (Spring Boot + Jsoup)]]></title>
            <link>https://velog.io/@yooni1231__/Implementing-Book-Info-Crawling-from-Yes24-Spring-Boot-Jsoup</link>
            <guid>https://velog.io/@yooni1231__/Implementing-Book-Info-Crawling-from-Yes24-Spring-Boot-Jsoup</guid>
            <pubDate>Tue, 24 Jun 2025 04:44:46 GMT</pubDate>
            <description><![CDATA[<p>I developed a book management system using React and Spring Boot, and I wanted to allow users to simply enter a book title and have the rest of the information (author, publisher, price, genre) automatically filled in. To achieve this, I implemented a web scraping feature that book data from Yes24, a major Kroean online bookstore.</p>
<h3 id="129314-problem">&amp;#129314 Problem</h3>
<p>Manually entering all book details during registration is incovenient for users. So, I decided to build a feature that :
-Takes only the book title input </p>
<ul>
<li>Scrapes the top result from Yes24&#39;s search results</li>
<li>Populates the form with book metadata automatically</li>
</ul>
<h3 id="128187-tech-stack">&amp;#128187 Tech Stack</h3>
<p>Jsoup - HTML parsing and web scraping library in Java
Spring Boot - REST API backend
React - Frontend interface (not covered here) 
REST API - Used to request and return scraped book data</p>
<h3 id="128220-final-controller">&amp;#128220 Final Controller</h3>
<pre><code>@GetMapping(&quot;/search-from-yes24&quot;)
public ResponseEntity&lt;BookDTO&gt; searchFromYes24(@RequestParam String title) {
    try {
        String encodedQuery = URLEncoder.encode(title, StandardCharsets.UTF_8);
        String searchUrl = &quot;https://www.yes24.com/Product/Search?domain=BOOK&amp;query=&quot; + encodedQuery;

        // Step 1: Parse search result page
        Document searchDoc = Jsoup.connect(searchUrl)
                .userAgent(&quot;Mozilla/5.0&quot;)
                .get();

        Element firstItem = searchDoc.selectFirst(&quot;div.itemUnit&quot;);
        if (firstItem == null) return ResponseEntity.notFound().build();

        String detailUrl = firstItem.selectFirst(&quot;a[href]&quot;).absUrl(&quot;href&quot;);
        if (detailUrl == null || detailUrl.isEmpty()) return ResponseEntity.status(502).build();

        // Step 2: Parse book detail page
        Document detailDoc = Jsoup.connect(detailUrl)
                .userAgent(&quot;Mozilla/5.0&quot;)
                .get();

        String bookTitle = detailDoc.selectFirst(&quot;h2.gd_name&quot;) != null
                ? detailDoc.selectFirst(&quot;h2.gd_name&quot;).text().trim()
                : &quot;Unknown Title&quot;;

        String author = detailDoc.selectFirst(&quot;span.gd_auth a&quot;) != null
                ? detailDoc.selectFirst(&quot;span.gd_auth a&quot;).text().trim()
                : &quot;Unknown Author&quot;;

        String priceText = detailDoc.selectFirst(&quot;em.yes_m&quot;) != null
                ? detailDoc.selectFirst(&quot;em.yes_m&quot;).text().replaceAll(&quot;[^0-9]&quot;, &quot;&quot;)
                : &quot;0&quot;;

        String publisher = detailDoc.selectFirst(&quot;span.gd_pub a&quot;) != null
                ? detailDoc.selectFirst(&quot;span.gd_pub a&quot;).text().trim()
                : &quot;Unknown Publisher&quot;;

        String genre = &quot;Unknown Genre&quot;;
        Elements genreEls = detailDoc.select(&quot;div#infoset_goodsCate dl.yesAlertDl dt:contains(Category) + dd ul.yesAlertLi li a&quot;);
        if (!genreEls.isEmpty()) {
            genre = genreEls.last().text().trim();
        }

        // Build DTO
        BookDTO dto = BookDTO.builder()
                .title(bookTitle)
                .author(author)
                .publisher(publisher)
                .price(Double.parseDouble(priceText))
                .genre(genre)
                .build();

        return ResponseEntity.ok(dto);

    } catch (Exception e) {
        e.printStackTrace();
        return ResponseEntity.status(500).build();
    }
}
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[데이터베이스 종류 ]]></title>
            <link>https://velog.io/@yooni1231__/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%A2%85%EB%A5%98</link>
            <guid>https://velog.io/@yooni1231__/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%A2%85%EB%A5%98</guid>
            <pubDate>Thu, 29 May 2025 22:40:08 GMT</pubDate>
            <description><![CDATA[<p>졸업 프로젝트 중 intellij와 mysql을 연결하면서 기존 사용하던 oracle database developer와의 차이가 궁금해져 정리해봅니다. </p>
<p>데이터 베이스의 종류</p>
<table>
<thead>
<tr>
<th>분류 기준</th>
<th>종류</th>
<th>특징 및 사용 예시</th>
</tr>
</thead>
<tbody><tr>
<td><strong>관계형 (RDBMS)</strong></td>
<td><strong>MySQL, PostgreSQL, Oracle, MariaDB</strong></td>
<td>테이블 기반 구조, SQL 사용, 트랜잭션 및 정합성 중시<br>예: ERP, 은행, 전자상거래 시스템</td>
</tr>
<tr>
<td><strong>키-값 (Key-Value)</strong></td>
<td><strong>Redis, DynamoDB, Riak</strong></td>
<td>단순한 키-값 구조, 빠른 조회 성능<br>예: 캐시 시스템, 세션 저장소</td>
</tr>
<tr>
<td><strong>문서형 (Document)</strong></td>
<td><strong>MongoDB, CouchDB, Firebase</strong></td>
<td>JSON 형태 문서 저장, 유연한 스키마<br>예: CMS, 사용자 데이터 저장</td>
</tr>
<tr>
<td><strong>열 기반 (Column-Family)</strong></td>
<td><strong>Apache Cassandra, HBase</strong></td>
<td>열 단위로 데이터 저장, 대규모 분석에 적합<br>예: 로그 분석, IoT 데이터 수집</td>
</tr>
<tr>
<td><strong>그래프형</strong></td>
<td><strong>Neo4j, Amazon Neptune</strong></td>
<td>노드와 간선 기반, 관계 중심 데이터 처리에 강점<br>예: 소셜 네트워크, 추천 시스템</td>
</tr>
<tr>
<td><strong>시계열형</strong></td>
<td><strong>InfluxDB, TimescaleDB</strong></td>
<td>시간에 따른 데이터 저장 최적화<br>예: 센서 로그, 서버 모니터링</td>
</tr>
<tr>
<td><strong>객체 지향형</strong></td>
<td><strong>db4o, ObjectDB</strong></td>
<td>객체 자체를 저장, OOP와의 연계 강함<br>예: 복잡한 객체 모델링 시스템</td>
</tr>
<tr>
<td><strong>멀티모델</strong></td>
<td><strong>ArangoDB, OrientDB</strong></td>
<td>다양한 모델 (문서+그래프+키-값 등) 혼합 지원<br>예: 유연한 구조의 복합 시스템</td>
</tr>
</tbody></table>
<p>Oracle vs Mysql
| 구분          | <strong>Oracle Database Developer</strong>            | <strong>MySQL</strong>                    |
| ----------- | ---------------------------------------- | ---------------------------- |
| <strong>개발사</strong>     | Oracle Corporation                       | MySQL AB (현재 Oracle이 인수)     |
| <strong>라이선스</strong>    | 상용 (유료) / 무료 버전 있음 (XE)                  | 오픈소스 (GPL) + 상용 (Enterprise) |
| <strong>성능</strong>      | 대규모 트랜잭션, 복잡한 비즈니스 로직에 최적화               | 빠른 응답, 웹/중소규모 서비스에 적합        |
| <strong>지원 언어</strong>   | PL/SQL (고급 기능 지원)                        | SQL, 프로시저는 상대적으로 단순          |
| <strong>스토리지 엔진</strong> | 자체 스토리지 (고성능, 안정성 중점)                    | InnoDB (기본), MyISAM 등 선택 가능  |
| <strong>트랜잭션 지원</strong> | 완전한 ACID 보장, 고성능 트랜잭션                    | InnoDB 엔진 기준 ACID 지원         |
| <strong>보안 기능</strong>   | 고급 보안: 행 수준 보안, 데이터 마스킹 등                | 기본적인 사용자 인증 및 권한 제어          |
| <strong>운영 도구</strong>   | Oracle SQL Developer, Enterprise Manager | MySQL Workbench, CLI         |
| <strong>확장성</strong>     | 수평/수직 확장 우수, 고가용성 구성 쉬움                  | 수평 확장에 제약, Replication 기반    |
| <strong>적합한 사용처</strong> | 대기업, 금융, 공공기관, ERP 시스템                   | 스타트업, 웹서비스, 교육용 프로젝트         |</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Uvicorn 이란 ? ]]></title>
            <link>https://velog.io/@yooni1231__/Uvicorn-%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@yooni1231__/Uvicorn-%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Thu, 29 May 2025 20:02:14 GMT</pubDate>
            <description><![CDATA[<p>모델 파일을 실행시킬 때 uvicorn 을 사용하여 실행시키게 됩니다. </p>
<p>FastaAPI나 Starlette로 웹 api를 개발한 경험이 있다면 아마 다음 명령어를 실행해본 적이 있을 것입니다. </p>
<p>uvicorn main:app --reload </p>
<p>이 명령어 속에는 비동기 python 웹 프레임 워크의 핵심이 있습니다. 
오늘은 FastAPI를 실서비스로 구동할 때 필수로 사용하는 uvicorn에 대해 작성해보겠습니다. </p>
<p>🌐 Uvicorn이란?</p>
<p>Uvicorn은 python의 비동기 웹 프레임워크를 실행하기 위한 agsi 서버입니다. 쉽게 말해, fastapi로 만든 api를 실제 웹 요청을 받을 수 있는 &quot;서버&quot;형태로 실행해주는 엔진입니다.</p>
<p>🔍 왜 WSGI가 아니라 ASGI인 이유?</p>
<p>전통적인 Django나 Flask 에서는 wsgi라는 동기 기반 표준을 사용합니다. 하지만 비동기 처리, websocket, 스트리밍 api 같은 실시간 기능을 지원하려면 AGSI 라는 새로운 비동기 표준이 필요합니다.</p>
<p>WSGI: 한 번에 하나의 요청만 처리 ( 동기)
AGSI: 여러 요청을 동시에 처리 가능 ( 비동기 ) </p>
<p>따라서 FastAPI와 같은 처리 프레이무어크는 AGSI 서버인 uvicorn을 사용해야합니다. </p>
<p>⚙️  Uvicorn 기본 실행 방법 </p>
<p>옵션
main:app main.py파일의 app = FastAPI()객체
--host 0.0.0.0 외부 접속 허용
--port 8000 사용할 포트
--reload 코드 수정 시 자동 재시작 ( 개발 환경에서 유용 ) </p>
<p>Uvicorn은 그 자체로 조용한 조력자이지만, FastAPI의 성능과 실시간성을 살리는 데 필수적인 존재입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[FastAPI와 Spring Boot를 연동한 AI 기반 도슨트 설명 전달 시스템 구축하기 ]]></title>
            <link>https://velog.io/@yooni1231__/FastAPI%EC%99%80-Spring-Boot%EB%A5%BC-%EC%97%B0%EB%8F%99%ED%95%9C-AI-%EA%B8%B0%EB%B0%98-%EB%8F%84%EC%8A%A8%ED%8A%B8-%EC%84%A4%EB%AA%85-%EC%A0%84%EB%8B%AC-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@yooni1231__/FastAPI%EC%99%80-Spring-Boot%EB%A5%BC-%EC%97%B0%EB%8F%99%ED%95%9C-AI-%EA%B8%B0%EB%B0%98-%EB%8F%84%EC%8A%A8%ED%8A%B8-%EC%84%A4%EB%AA%85-%EC%A0%84%EB%8B%AC-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 29 May 2025 19:53:52 GMT</pubDate>
            <description><![CDATA[<p>이전 포스트까지 AI 모델이 작품 속 객체를 인식하고 설명을 생성한 뒤, 해당 정보를 FAISS 데이터 베이스에 전달하는 구조를 만들었습니다. 
이 시스템은 FAST API와 SPRING BOOT를 연동하여 동작하도록 연결 할 것 이며 실시간 응답성과 인증 보안을 동시에 고려해 설계하였습니다. </p>
<p>전체 흐름 요약</p>
<ol>
<li>사용자가 이미지 클릭 -&gt; FAST API 서버로 요청 전송</li>
<li>FAST API는 YOLO/CLIP 기반으로 객체 설명 전송</li>
<li>JWT 인증 토큰과 함께 설명을 SPRING BOOT 로 전송 (POST)</li>
<li>SPRING BOOT는 Painting Id 유효성 검증 후 db에 설명 저장</li>
</ol>
<p>3/4 가 이번 포스트에서 다룰 내용입니다. </p>
<h4 id="fast-api에서-spring-boot로-post-전송-예시">FAST API에서 SPRING BOOT로 POST 전송 예시</h4>
<pre><code>@app.post(&quot;analyze&quot;)
def analyze_click(req:AnalyzeClickRequest):
    description = generate_descriptoin(req.image_id)
    response = requests.post(
        &quot;http://localhost:8080/api/model/response&quot;,
        json = { &quot;paintingId&quot;:req.image_id, &quot;description&quot;:description},
        headers = {&quot;Authorization&quot;: ACCESS_TOKEN} #JWT 인증 헤더 포함
        )
        return { &quot;status&quot; : response.status_code }

</code></pre><p> --&gt; AI가 생성한 설명을 SPRING 서버에 POST 방식으로 전송합니다.이때 , 보안 처리를 위해 JWT 토큰을 함께 헤더에 담습니다. </p>
<p> ! JWT 란? </p>
<pre><code>JSON WEB TOKEN 는 사용자 인증 정보를 안전하게 주고 받기 위한 디지털 토큰입니다. 
로그인 이후 서버가 클라이언트에게 토큰을 발급하면, 
이후 요청 시 이 토큰을 HTTP HEADER에 포함시켜 서버는 유저를 식별할 수 있습니다. </code></pre><p>   구성: 헤더, 내용, 서명으로 이루어진 문자열
   장점: 세션 관리 불필요, 서버가 사용자 상태를 저장하지 않아도 됨 
          분산 시스템에 적합
        가볍고 빠름</p>
<p>  ! 비동기 처리란?</p>
<pre><code>  하나의 작업이 끝날때까지 기다리지 않고, 다른 작업을 병렬로 처리할 수 있는 방식입니다. 
FAST API와 같은 프레임워크는 비동기 기반 서버 ( ASGI ) 를 사용해 다음을 가능하게 합니다. 

 - 여러 요청 동시 처리
 - 응답 대기 중에도 다른 요청 처리 가능
 - 서버 자원을 효율적으로 사용</code></pre><pre><code>    @app.post(&quot;/async.process&quot;)
    async def handle_asnyc():
    result = await slow_task()
    return result
</code></pre><h4 id="spring-boot-서버에서-설명-저장">SPRING BOOT 서버에서 설명 저장</h4>
<pre><code>@PostMapping(&quot;/api/model/response&quot;)
public ResponseEntity&lt;?&gt; receiveDescription(@RequestBody ObjectDescriptionRequest request){
Painting painting = paintingRepository.findById(reequest.getPaintingID())
.orElseThrow() -&gt; new IllegalArgumentException(&quot;해당 그림 없음&quot;));
painting.setBackground(request.getDescription());
return ResponseEntity.ok().build();
}
</code></pre><p>이 연동 시스템은 비동기 요청 처리, jwt 인증, 모델-백엔드 간 restful 통신 , db 저장 처리 까지 실제 프로덕션 환경에서 요구되는 다양한 기능을 통합하였습니다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AI Docent System for Art Exhibitions (6-2)
]]></title>
            <link>https://velog.io/@yooni1231__/AI-Docent-System-for-Art-Exhibitions</link>
            <guid>https://velog.io/@yooni1231__/AI-Docent-System-for-Art-Exhibitions</guid>
            <pubDate>Wed, 21 May 2025 03:42:48 GMT</pubDate>
            <description><![CDATA[<p>Object Dectection -&gt; Embedding -&gt; Faiss -&gt; LlaMa Explanation pipeline</p>
<p>In our project, we aim to develop an AI docent system that automatically provides descriptions for specific objects detected within artwork images.</p>
<p>Pipeline Overview</p>
<p>[ full artwork image ] </p>
<p>Yolov8 segmentation 
-&gt;</p>
<p>[object Dectection &amp; crop]</p>
<p>CLIP embedding 
-&gt; </p>
<p>[ vectorized image ]
FAISS Indexing 
-&gt;</p>
<p>[ query -&gt; object retrieval ]
Llama-based generation
-&gt; </p>
<p>[ visitor-friendly docent explanation ]</p>
<ol>
<li>Ojbect Detection and cropping with yolov8</li>
</ol>
<p>We use the yolov8-seg.pt model to detect objects within a given artwork image. 
Unlike simple bounding box detection, this model utilizes segmentation masks, allowing us to precisely crop each object based on its actual shape.</p>
<ol start="2">
<li>Embedding Cropped Objects Using CLIP</li>
</ol>
<p>Each cropped object iamge is passed through CLIP to convert it into a semantic vector.
This enables us to later retrieve sementically similar objects based on the visitor&#39;s query.</p>
<p>What is a Semantic Vector?</p>
<p>A semantic vector is a numerical representation that captures the meaning of text, images, or othre human-understandable content in a form that machines can interpret.
In similar terms: it&#39;s an array of numbers that represents meaning.</p>
<p>For example:</p>
<p>CAT :     [0.8, 0.2, 0.5]
DOG  :    [0.79, 0.21, 0.52]
CAR : [0.1, 0.9, 0.3]</p>
<p>Cat and Dog have similar vector because they are semantically related.
Car is conceptually different , so its vector is far apart.</p>
<p>The similarity between these vectors tells us how closely related the meanings are.</p>
<p>CLIP is trained to embed both images and text into the same semantic space.</p>
<p>Example: 
&quot;an apple on the table&quot; -&gt; [text vector]
image of an actual apple -&gt; [image vector] </p>
<p>These are trained to be close in vector space.
So when a user asks, &quot;where is the apple?&quot; , we convert the question into a vector and use FAISS to find the closest image embedding - an then explain it.</p>
<ol start="3">
<li>Indexing Semantic Vectors with FAISS</li>
</ol>
<p>The image embeddings obtained from CLIP are indexed using FAISS. This enables fast, approximate nearest neighbor search to retrieve the most semantically similar objects later.</p>
<ol start="4">
<li>Query-based Retrieval and Description generation via LLaMA</li>
</ol>
<p>When a user submits a natural-language query, the following steps occur: </p>
<ol>
<li>The query is converted into a semantic vector using CLIP&#39;s text encoder.</li>
<li>FAISS searches for the most similar object vectors.</li>
<li>Metadata for the top result (labe, description, etc) is retrieved.</li>
<li>This information is passed into a prompt, which is then fed into LLaMA to generate a natural, human-friendly explanation.</li>
</ol>
<p>This creates a dynamic docent experience where visitors can ask questions or click on an object and receive personalized explanations generated on the spot. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[이미지 객체 인식부터 LLAMA 기반 설명 생성  ]]></title>
            <link>https://velog.io/@yooni1231__/%EC%9D%B4%EB%AF%B8%EC%A7%80-%EA%B0%9D%EC%B2%B4-%EC%9D%B8%EC%8B%9D%EB%B6%80%ED%84%B0-LLAMA-%EA%B8%B0%EB%B0%98-%EC%84%A4%EB%AA%85-%EC%83%9D%EC%84%B1</link>
            <guid>https://velog.io/@yooni1231__/%EC%9D%B4%EB%AF%B8%EC%A7%80-%EA%B0%9D%EC%B2%B4-%EC%9D%B8%EC%8B%9D%EB%B6%80%ED%84%B0-LLAMA-%EA%B8%B0%EB%B0%98-%EC%84%A4%EB%AA%85-%EC%83%9D%EC%84%B1</guid>
            <pubDate>Tue, 20 May 2025 14:02:42 GMT</pubDate>
            <description><![CDATA[<p>우리 프로젝트에서는 특정 객체(사과, 꽃병, 인물 등) 에 대한 설명을 자동으로 제공해주는 AI 도슨트를 만들고자 합니다.</p>
<p>전체 구조 요약</p>
<p>[전체 작품 이미지] </p>
<p>YOLOV8 segmentation
-&gt;</p>
<p>[객체 감지 및 CROP] </p>
<p>clip 임베딩 
-&gt;</p>
<p>[백터화된 이미지] </p>
<p>FAISS Index 저장
-&gt;</p>
<p>[ 질의 -&gt; 관련 객체 검색] </p>
<p>LLaMA 문장 생성
-&gt;</p>
<p>[관람객 맞춤 도슨트 설명 ]</p>
<ol>
<li>YOLOv8 기반 객체 감지 및 crop</li>
</ol>
<p>먼저, yolov8-seg-pt 모델을 활용하여 작품 이미지에서 작품을 감지합니다. 이때, bouning box가 아니라 segmentation mask를 사용하여 마스크 기반으로 객체만 추출합니다. </p>
<ol start="2">
<li>Clip으로 객체 이미지 임베딩</li>
</ol>
<p>각 crop된 객체 이미지에 대해 clip을 사용하여 <strong>시멘틱 벡터</strong>로 변환합니다. 
나중에 사용자 질문과 유사한 의미의 객체를 검색할 수 있습니다. </p>
<p> ** 시멘틱 벡터란 ? **</p>
<p> 단어나 문장, 이미지처럼 인간이 이해하는 의미를 컴퓨터가 수치적으로 다룰 수 있게 만든 벡터 표현을 말합니다. 
 쉽게 말해, 의미를 담고 있는 숫자의 배열을 의미합니다. </p>
<p> ex) 고양이 : [0.8, 0.2, 0.5] , 강아지 [0.79, 0.21, 0.52] 자동차 [0.1 , 0.9, 0.3] </p>
<p> 고양이와 강아지는 유사한 동물이기 때문에 벡터값이 비슷, 자동차는 전혀 다른 의미라서 벡터가 다름 </p>
<p> --&gt; 벡터 간의 유사도를 계산하면 서로 의미적으로 얼마나 비슷한지를 판단할 수 있음</p>
<p> clip은 이미지와 텍스트를 같은 의미 공간에 임베딩함 
 &quot;an apple on the table&quot; -&gt; [텍스트 시멘틱 벡터]
 실제 사과 사진 -&gt; [이미지 시멘틱 벡터], 이 둘이 서로 가까운 위치에 있도록 학습함</p>
<p> 사용자가 &#39;사과가 어딨지&#39; 라고 질문하면 벡터로 바꿔 faiss에서 가장 가까운 이미지 벡터를 찾고, 그에 대한 설명을 해줄 수 있습니다. </p>
<ol start="3">
<li>FAISS를 이용한 벡터 인덱싱 </li>
</ol>
<p>CLIP 임베딩된 벡터들을 FAISS 를 사용하여 인덱스에 저장</p>
<p>이 인덱스를 통해 나중에 의미적으로 유사한 객체를 빠르게 검색할 수 있음</p>
<ol start="4">
<li><p>질의 기반 객체 검색 및 LLaMA로 설명 생성</p>
<ol>
<li>CLIP 텍스트 임베딩으로 질문을 벡터화</li>
<li>FAISS로 가장 유사한 객체 이미지 검색</li>
<li>메타데이터로부터 label + 객체 설명 가져오기 </li>
<li>llama에 prompt와 함께 전달 -&gt; 자연어 생성</li>
</ol>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[LangChain과 LLaMA로 경량형 RAG 시스템 만들기
]]></title>
            <link>https://velog.io/@yooni1231__/LangChain%EA%B3%BC-LLaMA%EB%A1%9C-%EA%B2%BD%EB%9F%89%ED%98%95-RAG-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@yooni1231__/LangChain%EA%B3%BC-LLaMA%EB%A1%9C-%EA%B2%BD%EB%9F%89%ED%98%95-RAG-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Mon, 21 Apr 2025 05:05:57 GMT</pubDate>
            <description><![CDATA[<p>요즘 RAG(Retrieval-Augmented Generation) 기반의 시스템을 계속 만지다가, 문득 이런 생각이 들었습니다.
&quot;꼭 GPT-4 같은 대형 모델을 써야만 쓸 만한 RAG가 만들어질까?&quot;</p>
<p>비용이나 응답 속도, 모델의 자율성까지 고려해보면 GPT 계열에 의존하지 않고도 꽤 괜찮은 결과를 낼 수 있겠다는 생각이 들어, 이번엔 직접 LangChain + LlamaIndex + LLaMA 조합으로 경량형 RAG 시스템을 구성해봤습니다.</p>
<p>✅ 목표
이번 프로젝트에서 세운 목표는 다음과 같습니다:</p>
<p>로컬 환경에서 완전히 작동할 것</p>
<p>경량화된 모델로도 충분히 답변이 가능할 것</p>
<p>파이프라인은 최대한 단순하게 구성할 것</p>
<p>벡터 검색 성능도 일정 수준 확보할 것</p>
<h3 id="사용한-구성-요소">사용한 구성 요소</h3>
<p>역할    도구
LLM    LLaMA2 / LLaMA3 (gguf or HF format)
파이프라인 구성    LangChain
문서 검색 / 인덱싱    LlamaIndex
벡터 DB    FAISS (또는 Chroma, 선택 가능)
임베딩 모델    Instructor, MiniLM 등 경량 모델 사용</p>
<p>✅ 문서 분할 전략
문서를 일정한 크기로 나누는 작업부터 신중히 설계하였습니다.
한 번에 너무 많은 텍스트를 넣으면 검색 정확도가 떨어지고, 너무 작게 분할하면 문맥이 끊어져 답변이 부정확해졌습니다.
실험 결과, 512~1024토큰 단위로 나누는 것이 성능과 효율성의 균형이 좋았습니다.
또한 슬라이딩 윈도우 방식으로 중첩 분할을 적용해 문맥의 연속성을 유지하였습니다.</p>
<p>✅ 임베딩 모델 선택
문서와 질의를 벡터로 변환하는 임베딩 모델은 시스템의 핵심이었습니다.
all-MiniLM-L6-v2 모델을 주로 사용하였으며, CPU에서도 빠르게 작동하고 검색 정확도가 안정적이었습니다.
또한 BAAI의 bge-small-en 모델도 성능 측면에서 뛰어나 활용해보았습니다.</p>
<p>문서 임베딩과 질의 임베딩을 따로 최적화할 수 있는 e5-small 모델 계열도 테스트하였으나, 구현 복잡도를 고려해 기본 모델로 유지하였습니다.</p>
<p>✅ 벡터 검색 시스템
검색 성능은 전적으로 벡터 DB에 달려 있었기 때문에, FAISS를 선택하여 로컬에서 빠르고 안정적으로 검색이 가능하도록 구성하였습니다.
소규모 문서(수천 단위)에는 적합했고, 불필요한 외부 서비스 없이도 자체 호스팅이 가능하다는 점에서 장점이 있었습니다.
문서 임베딩 결과는 로컬에 저장하여, 시스템 시작 시 재사용할 수 있도록 처리하였습니다.</p>
<p>✅ 프롬프트 구성
문서 검색 후 LLM에 전달할 프롬프트는 명확하고 간결하게 구성하였습니다.
“다음 문서를 참고하여 질문에 답하십시오” 형태의 시스템 메시지를 고정 프롬프트로 사용하였고, 검색된 문서 2~4개를 컨텍스트로 붙였습니다.
문서 수가 많아지면 오히려 모델이 집중하지 못했기 때문에, 적절한 문서 수 제한이 성능 향상에 도움이 되었습니다.</p>
<p>✅ 경량 LLM 구성
LLM은 LLaMA-2 7B Chat 모델의 quantized 버전(q4_K_M)을 사용하였습니다.
llama-cpp-python을 통해 로컬에서 구동하였고, n_threads 및 context window 등의 파라미터를 조정하여 최적화를 진행하였습니다.</p>
<p>GPU 없이도 동작이 가능해야 했기 때문에 quantized 모델은 필수였습니다.
LLaMA 외에도 Mistral, Phi-2 등도 테스트하였으며, 모델마다 응답 스타일과 속도에 차이가 존재하였습니다.</p>
<p>✅ 파이프라인 설계
LangChain과 LlamaIndex를 조합하여 전체 RAG 파이프라인을 구성하였습니다.
다만 LangChain의 복잡한 체인 구조는 지양하고, 가능한 한 단순한 형태로 구성하였습니다.
질의 → 검색 → 컨텍스트 구성 → LLM 호출 → 응답 출력의 최소 흐름으로 유지하여 성능 저하를 방지하였습니다.</p>
<p>📌 결론
이번 RAG 시스템을 통해 확인한 바는 다음과 같습니다.</p>
<p>경량 모델로도 충분히 유의미한 질의응답 시스템을 구현할 수 있었습니다.</p>
<p>성능을 좌우하는 것은 모델보다도 문서 분할, 검색 정확도, 프롬프트 구성이었습니다.</p>
<p>전체 파이프라인은 단순할수록 유지 보수가 쉬웠으며, 모델 특성을 고려한 조율이 매우 중요했습니다.</p>
<p>향후에는 웹 인터페이스 연동 및 실시간 문서 반영 기능을 추가해, 내부 문서 기반의 실용 챗봇 형태로 확장할 계획입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[LLM+RAG 이용 미술관 도슨트 시스템 — 하드웨어 준비편]]></title>
            <link>https://velog.io/@yooni1231__/LLMRAG-%EC%9D%B4%EC%9A%A9-%EB%AF%B8%EC%88%A0%EA%B4%80-%EB%8F%84%EC%8A%A8%ED%8A%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%95%98%EB%93%9C%EC%9B%A8%EC%96%B4-%EC%A4%80%EB%B9%84%ED%8E%B8</link>
            <guid>https://velog.io/@yooni1231__/LLMRAG-%EC%9D%B4%EC%9A%A9-%EB%AF%B8%EC%88%A0%EA%B4%80-%EB%8F%84%EC%8A%A8%ED%8A%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%95%98%EB%93%9C%EC%9B%A8%EC%96%B4-%EC%A4%80%EB%B9%84%ED%8E%B8</guid>
            <pubDate>Sat, 12 Apr 2025 05:55:51 GMT</pubDate>
            <description><![CDATA[<p>미술관이나 전시 공간에서 눈과 귀가 되어주는 스마트 도슨트 시스템을 직접 만들 수 있다면 어떨까요? 
이번 글에서는 라즈베리파이5를 이용해 LLM+RAG 기반 스마트 도슨트 시스템을 만들기 위한 하드웨어 구성 시나리오를 작성해보겠습니다. </p>
<p>아직 제작 전이고, 제작 과정을 블로그에 담아볼 예정입니다. </p>
<p>우선 완성된 도슨트를 얼굴 부위에 밀착하여 착용해야하기 때문에 안전상의 문제로 라즈베리파이는 센서 허브와 인터페이스 장치 역할로 사용하기로 결정하였습니다. 
라즈베리파이로 LLM과 RAG를 구동하면 발열 문제가 있을 것으로 예상되기 때문이죠. </p>
<h4 id="즉-라즈베리파이는-사용자의-음성-입력-카메라-감지-ui-출력을-담당하고">즉, 라즈베리파이는 사용자의 음성 입력, 카메라 감지, UI 출력을 담당하고,</h4>
<h4 id="llm--rag-연산은-스마트폰-또는-클라우드-서버에서-수행됩니다">LLM + RAG 연산은 스마트폰 또는 클라우드 서버에서 수행됩니다.</h4>
<p>우선 예상 준비물은 다음과 같습니다. </p>
<table border="1" cellpadding="8" cellspacing="0">
  <thead>
    <tr>
      <th></th>
      <th>구성품</th>
      <th>역할</th>
      <th>연결 방식 / 포트</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>🧠</td>
      <td><strong>Raspberry Pi 5 (4GB)</strong></td>
      <td>메인 컨트롤러</td>
      <td>전원 / GPIO / CSI / USB 등</td>
    </tr>
    <tr>
      <td>🔌</td>
      <td><strong>PD 27W 아답터</strong></td>
      <td>전원 공급</td>
      <td>USB-C 포트</td>
    </tr>
    <tr>
      <td>💾</td>
      <td><strong>microSD 64GB + 32GB</strong></td>
      <td>OS 및 저장소</td>
      <td>microSD 슬롯</td>
    </tr>
    <tr>
      <td>🎙️</td>
      <td><strong>ReSpeaker 2-Mics Pi HAT</strong></td>
      <td>음성 입력(마이크) + 출력(스피커)</td>
      <td>GPIO 헤더 (I2S / I2C 통신)</td>
    </tr>
    <tr>
      <td>📷</td>
      <td><strong>Pi 카메라 모듈 3</strong></td>
      <td>컬러 인식 / QR 코드 등</td>
      <td>CSI 포트 0 (22핀)</td>
    </tr>
    <tr>
      <td>🌙</td>
      <td><strong>NOIR IR 카메라</strong></td>
      <td>적외선 감지 / 야간 인식</td>
      <td>CSI 포트 1 (FPC 케이블 필요)</td>
    </tr>
    <tr>
      <td>🔌</td>
      <td><strong>FPC 카메라 케이블 (22P → 15P)</strong></td>
      <td>Pi5와 구형 카메라 연결</td>
      <td>CSI 어댑터 케이블</td>
    </tr>
  </tbody>
</table>


<p><img src="https://velog.velcdn.com/images/yooni1231__/post/81bc4f52-3a11-4afc-9ead-6e901be43c52/image.png" alt=""></p>
<p>라즈베리 5의 경우 CSI( 라즈베리파이 전용 카메라를 연결하기 위한 포트) 는 2개를 제공하고 있고,MIPI CSI-2 프로토콜을 사용하여 고속 영상 데이터를 저전력으로 전송합니다. </p>
<p>라즈베리 5부터는 CSI 포트가 변경(22핀/0.5mm 피치 FPC 커넥터) 되어 기존 라즈베리파이 카메라 연결구인 15핀을 연결하기 위해서는 변환 케이블이 필요합니다. </p>
<details>
  <summary>🔍 FPC란? 자세히 보기</summary>
  <p>
    FPC는 Flexible Printed Circuit의 줄임말로,<br>
    얇고 유연한 필름 위에 회로가 인쇄된 구조입니다.<br><br>

<pre><code>📌 라즈베리파이에서는 카메라나 디스플레이를 CSI/DSI 포트를 통해 연결할 때 필수적으로 사용되며,&lt;br&gt;
Pi 5에서는 22핀(0.5mm 피치)이기 때문에 기존 15핀 카메라와 연결하려면 변환 케이블이 필요합니다.&lt;br&gt;&lt;br&gt;
&lt;img src=&quot;https://tse4.mm.bing.net/th?id=OIP.JhOuHwPJCrowKyFnDsBO7QHaFj&amp;pid=Api&quot; 
   alt=&quot;라즈베리파이 FPC 카메라 케이블 연결 예시&quot; 
   width=&quot;500&quot; style=&quot;margin-top: 10px; border: 1px solid #ccc;&quot;&gt; 
&lt;p style=&quot;font-size: 0.9em; color: gray;&quot;&gt;
▲ 라즈베리파이와 카메라 모듈을 연결한 FPC 케이블 예시 (출처: Adafruit)  </code></pre>  </p>

</details>

<br>
<br>
<br>

<p>포트는 CAM0, CAM1으로 구분되며 CAM0의 경우 기본 카메라 포트 (보통 메인 카메라 연결)이고, CAM1은 보조 카메라 연결에 사용합니다. (IR 카메라등 ) </p>
<h4 id="🌐-통신-흐름도">🌐 통신 흐름도</h4>
<h4 id="사용자-음성">[사용자 음성]</h4>
<p>   ↓</p>
<h4 id="respeaker-마이크">[ReSpeaker 마이크]</h4>
<p>   ↓</p>
<h4 id="raspberry-pi-5-↔-wi-fi-↔-스마트폰-or-클라우드-llm--rag">[Raspberry Pi 5] ↔ Wi-Fi ↔ [스마트폰 or 클라우드 LLM + RAG]</h4>
<p>   ↓</p>
<h4 id="tts-음성-출력--카메라-ui-출력">[TTS 음성 출력 / 카메라 UI 출력]</h4>
<p>통신 흐름의 경우 사용자가 음성으로 질문을 하고, PI가 이를 녹음 후 스마트폰 서버에 전송합니다. 이후 서버에서 LLM+RAG 응답을 생성하고,PI로 다시 전송합니다. PI가 결과를 TTS로 읽거나 스마트폰 어플리케이션 디스플레이에 출력하게 됩니다. </p>
<br>
<br>


<p>다음 글에서는 라즈베리파이5에서 카메라, 마이크 입력을 수집하고 llm 서버와 통신항여 응답받는 방법을 소개하겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Search Smarter, Generate Better: The Power of Advanced RAG]]></title>
            <link>https://velog.io/@yooni1231__/Search-Smarter-Generate-Better-The-Power-of-Advanced-RAG</link>
            <guid>https://velog.io/@yooni1231__/Search-Smarter-Generate-Better-The-Power-of-Advanced-RAG</guid>
            <pubDate>Sat, 12 Apr 2025 04:54:27 GMT</pubDate>
            <description><![CDATA[<p>📌 “Searching well is half the battle”
Turns out that’s completely true for RAG too.</p>
<p>🧑‍💻 Intro: What is RAG?
RAG stands for Retrieval-Augmented Generation,
a method that augments LLMs (like GPT-4, Claude, Mistral, etc.) with search capabilities.</p>
<p>At first, I thought:</p>
<p>&quot;Isn&#39;t this just feeding documents into the LLM after searching?&quot;</p>
<p>But once I started studying it deeply… I realized it’s much more than that.</p>
<p>Advanced RAG isn’t just search + generation.
It’s an optimized pipeline that improves retrieval accuracy, context understanding, and trustworthiness of the responses.</p>
<p>In this post, I’ll break down what I’ve learned so far about:</p>
<p>What Advanced RAG is</p>
<p>Why we need it</p>
<p>What techniques make it powerful</p>
<p>📚 So what exactly is RAG?
RAG stands for Retrieval-Augmented Generation.</p>
<p>In simple terms, it means giving a language model the ability to look things up.</p>
<p>Take models like GPT-3.5 for example—
They&#39;re trained only up to 2023, so anything beyond that? They&#39;re clueless.</p>
<p>Imagine asking:</p>
<p>“What new policies were introduced in the 2024 elections?”</p>
<p>“What features were added in GPT-4.5?”</p>
<p>A base model won’t be able to answer these.</p>
<p>That’s where RAG comes in.
It lets the LLM pull in external documents, PDFs, web search results, and more—
giving it access to real-time and external knowledge.</p>
<p>📦 Basic (Naive) RAG: How it works
Here’s a simple version of how RAG works:</p>
<p>📄 Chunk documents and convert them into vector embeddings</p>
<p>🔍 Convert the user question into a vector and search for top-K similar chunks</p>
<p>🧠 Feed those chunks + question into the LLM to generate the final answer</p>
<p>Sounds easy, right? But real-world RAG has… issues.</p>
<p>⚠️ Limitations of Naive RAG
Area    Problem
Indexing    Poor parsing of PDFs, tables, or sections → information loss
Retrieval    Returns duplicates or irrelevant chunks, misses key content
Generation    Bad context = misleading or incorrect answers
Even with a powerful LLM, bad retrieval ruins everything 😭</p>
<p>🌟 Enter: Advanced RAG
Advanced RAG addresses those limitations with a 4-stage optimized pipeline:</p>
<p>🧱 Advanced RAG Framework
Pre-Retrieval → Document parsing, query rewriting</p>
<p>Retrieval → Better search (hybrid, fine-tuned embeddings)</p>
<p>Post-Retrieval → Reranking, compression, filtering</p>
<p>Generation → Optimized prompting and response generation</p>
<p>🔍 Stage 1: Pre-Retrieval
🧾 PDF Parsing
PDFs are not plain text—they’re layout commands.
If you extract text directly, you lose formatting, tables, and flow.</p>
<p>Solutions:</p>
<p>pypdf: Rule-based, simple</p>
<p>Unstructured, LayoutParser: DL-based, can detect structure</p>
<p>PP-StructureV2: Extracts semantic info from layouts</p>
<p>✍️ Query Rewriting
Real user questions are often vague or multi-topic, which ruins search accuracy.</p>
<p>Solutions:</p>
<p>Step-Back Prompting: Generalize the question</p>
<p>HyDE: Generate pseudo-docs to embed and search</p>
<p>Query2Doc: Rewrites the query like a document</p>
<p>ITER-RETGEN: Alternating retrieval &amp; generation for refinement</p>
<p>🔗 Context Expansion
One sentence isn’t enough—expand to include surrounding context.</p>
<p>Techniques:</p>
<p>Sentence Window Retrieval: Add k sentences before/after</p>
<p>Parent Chunking: Group chunks into higher-level units</p>
<p>🔎 Stage 2: Retrieval
🧬 Hybrid Search
BM25: Keyword-based, great for precision</p>
<p>Vector Search: Embedding-based, great for semantics</p>
<p>💡 Combine both using RRF (Reciprocal Rank Fusion) → best of both worlds!</p>
<p>✂️ Stage 3: Post-Retrieval
🔄 Re-ranking
Even the top-K results may include junk.
So we re-rank based on relevance or importance.</p>
<p>Tools:</p>
<p>bge-reranker, Cohere, RankGPT</p>
<p>📉 Prompt Compression
LLMs have token limits. Example: GPT-3.5 ≈ 4,000 tokens.</p>
<p>Solutions:</p>
<p>Selective Context: Keep only informative content</p>
<p>LLMLingua, AutoCompressor, RECOMP: Token-level or semantic compression</p>
<p>🧼 Filtering
Remove duplicates, irrelevant, or low-trust chunks.</p>
<p>Models:</p>
<p>FiD-TF, Self-RAG, CRAG: Filter at the token or chunk level</p>
<p>🧠 Stage 4: Generation
🛠 Advanced Generation Techniques
DSP: Multi-query + multi-doc → merged answer</p>
<p>PRCA: RL-based generation refinement</p>
<p>REPLUG: Inserts search results directly into prompts</p>
<p>RECITE: Generate multiple answers → majority vote
✅ Evaluation Matters!
How do you know if your RAG pipeline is actually working?</p>
<p>You can’t just rely on “it feels right.” You need structured evaluation.</p>
<p>Here are some of the best tools and metrics used to evaluate RAG systems:</p>
<p>📊 Tools &amp; Frameworks for RAG Evaluation
Tool / Method    Purpose
Ragas    Evaluate factual accuracy, retrieval precision, and generation faithfulness
LangSmith    Tracks individual steps inside retrieval and generation chains (LangChain-friendly)
OpenAI Cookbook    Offers scripts and guidelines for evaluating performance by category
Helm    Holistic Evaluation of Language Models, useful for benchmarking
LlamaIndex Evaluation    Measures document coverage and response relevance
BERTScore / ROUGE / BLEU    Traditional NLP metrics, can help for generation fidelity
User Feedback Loop    In production systems, nothing beats real user voting and correction tracking
🧪 Key Evaluation Metrics
Metric    What It Measures
Context Precision    Did the retriever bring back relevant content?
Context Recall    Did it miss any important information?
Answer Faithfulness    Is the generated answer grounded in retrieved facts?
Answer Relevance    Does the answer actually address the question?
Latency    How fast is retrieval + generation? Important in real-time apps
🔁 Recap: Why Advanced RAG Matters
Basic RAG might be enough for demos or prototypes.
But if you want to build real-world LLM apps — like search assistants, internal tools, or voice docents — you need:</p>
<p>✅ Clean document ingestion
✅ Accurate and rich retrieval
✅ Efficient compression + reranking
✅ Faithful generation
✅ A tight feedback loop for evaluation</p>
<p>🚀 TL;DR: RAG, When Done Right, Changes the Game
Advanced RAG is more than &quot;search + generate.&quot;
It’s a full-stack retrieval-generation architecture.</p>
<p>Think of it as the “search engine” behind your LLM—
and just like real search engines, optimizing the pipeline is everything.</p>
<p>If you nail each stage — from chunking, to retrieval, to reranking, to compression, to generation —
your LLM can answer anything, grounded in your own data.</p>
<p>📌 Final Thoughts
“Just vector search and pass it to GPT” is where everyone starts.
But if you&#39;re serious about performance, trust, and user satisfaction, you’ll want:</p>
<p>✨ Pre-processing pipelines
🧠 Smart retrievers
⚙️ Modular evaluators
🤖 Agents that collaborate
📉 And generators that know what to leave out.</p>
<p>RAG isn&#39;t just a trick.
It’s a strategy.</p>
<p>References &amp; Further Reading
Advanced RAG — Part 10(<a href="https://medium.com/@vipra_singh/building-llm-applications-advanced-rag-part-10-ec0fe735aeb1">https://medium.com/@vipra_singh/building-llm-applications-advanced-rag-part-10-ec0fe735aeb1</a>)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[🤖 ChatGPT보다 똑똑한 RAG 만들기?! Advanced RAG 개념부터 기법까지  정리 
]]></title>
            <link>https://velog.io/@yooni1231__/ChatGPT%EB%B3%B4%EB%8B%A4-%EB%98%91%EB%98%91%ED%95%9C-RAG-%EB%A7%8C%EB%93%A4%EA%B8%B0-Advanced-RAG-%EA%B0%9C%EB%85%90%EB%B6%80%ED%84%B0-%EA%B8%B0%EB%B2%95%EA%B9%8C%EC%A7%80-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@yooni1231__/ChatGPT%EB%B3%B4%EB%8B%A4-%EB%98%91%EB%98%91%ED%95%9C-RAG-%EB%A7%8C%EB%93%A4%EA%B8%B0-Advanced-RAG-%EA%B0%9C%EB%85%90%EB%B6%80%ED%84%B0-%EA%B8%B0%EB%B2%95%EA%B9%8C%EC%A7%80-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Sun, 06 Apr 2025 00:49:59 GMT</pubDate>
            <description><![CDATA[<p>📌 &quot;검색만 잘해도 반은 먹고 들어간다&quot; — RAG에도 완벽히 적용되는 말인듯 싶습니다. </p>
<h2 id="🧑💻-intro-rag가-뭐예요">🧑‍💻 Intro: RAG가 뭐예요?</h2>
<p><strong>RAG (Retrieval-Augmented Generation)</strong>는
LLM(예: GPT-4, Claude, Mistral 등)에게 검색을 붙여주는 방식이에요.</p>
<p>처음에 RAG를 접했을 땐
“그냥 문서 검색해서 LLM에 넣는 거 아닌가?”</p>
<p>하지만 </p>
<p>고급 RAG는 단순 검색 + 생성이 아닙니다.
파이프라인 전반을 최적화해서
검색 정확도, 문맥 이해도, 응답의 신뢰도까지 다 높여주는 구조거든요.</p>
<p>이번 글에서는 제가 공부하면서 정리한
&quot;Advanced RAG가 뭔지&quot;, &quot;왜 필요한지&quot;, &quot;어떤 기술들이 쓰이는지&quot;
정리해보겠습니다. </p>
<h2 id="📚-rag가-뭔데요">📚 RAG가 뭔데요?</h2>
<p>RAG는 “Retrieval-Augmented Generation”의 줄임말로,
쉽게 말하면 LLM에게 ‘검색 기능’을 붙여주는 구조입니다.</p>
<p>예를 들어 GPT-3.5 같은 LLM은
2023년 이전까지의 지식만 학습돼 있어서
그 이후 생긴 정보는 모르고 있어요.</p>
<p>예를 들어,</p>
<p>&quot;2024년 총선에서 어떤 정책이 새로 나왔어요?&quot;
&quot;GPT-4.5에서 추가된 기능이 뭐예요?&quot;</p>
<p>이런 질문은 기본 GPT로는 답을 못 해요.</p>
<p>이럴 때 필요한 게 바로 RAG예요.
외부 문서, PDF, 웹 검색 결과 등을 LLM에 함께 넣어서
지금 이 순간의 정보까지 반영한 응답을 생성할 수 있게 해주는 기술입니다.</p>
<h2 id="📦-naive-rag는-이렇게-작동해요">📦 Naive RAG는 이렇게 작동해요</h2>
<p>기본적인 RAG는 다음과 같은 구조로 되어 있어요:</p>
<p>📄 문서를 청크(chunk) 단위로 잘라서 벡터로 변환</p>
<p>🔍 사용자의 질문도 벡터로 만들어서, 문서 벡터들과 비교해 Top-K 검색</p>
<p>🧠 검색된 청크를 LLM에게 함께 보내서 응답 생성</p>
<p>이게 기본 구조인데, 현실에서는 여러 문제가 발생합니다...</p>
<h2 id="⚠️-naive-rag의-한계">⚠️ Naive RAG의 한계</h2>
<p>항목    문제점
인덱싱    PDF나 보고서 같은 문서를 제대로 파싱 못 해서 정보가 유실됨
검색    중복된 내용만 나오거나, 중요한 내용을 못 찾는 경우 많음
생성    LLM이 잘못된 문맥을 받아서 오답을 내거나 편향된 정보 생성
특히 검색이 부정확하면,
아무리 좋은 LLM을 써도 이상한 답변이 나오게 되더라고요 😭</p>
<h2 id="🌟-그래서-등장한-advanced-rag">🌟 그래서 등장한 Advanced RAG</h2>
<p>Advanced RAG는 Naive RAG의 단점을 보완한 구조예요.
전체 과정을 다음 4단계로 나누고, 각 단계마다 최적화 기법을 적용합니다.</p>
<p>Advanced RAG의 4단계 구조
Pre-Retrieval: 검색 전 준비 (문서 파싱, 쿼리 정리 등)</p>
<p>Retrieval: 검색 최적화 (하이브리드, 임베딩 튜닝 등)</p>
<p>Post-Retrieval: 결과 압축, 재정렬, 필터링</p>
<p>Generation: 답변 생성 최적화 (다중 생성, 요약 등)</p>
<h2 id="🔍-1단계-pre-retrieval">🔍 1단계: Pre-Retrieval</h2>
<p>🧾 PDF 파싱
PDF는 단순한 텍스트가 아니라
<strong>&quot;어디에 어떻게 출력할지를 지시하는 명령어 덩어리&quot;</strong>에 가깝습니다.</p>
<p>그래서 그냥 텍스트로 추출하면
문단, 표, 그림이 다 깨지거나 줄바꿈이 엉망이 되죠.</p>
<h2 id="💡-해결-방법">💡 해결 방법:</h2>
<p>pypdf: 룰 기반 파서, 간단하지만 구조 인식 어려움</p>
<p>Unstructured, LayoutParser: 딥러닝 기반, 표/문단 인식 가능</p>
<p>PP-StructureV2: 문서 안의 핵심 정보까지 추출</p>
<h2 id="✍️-쿼리-재작성">✍️ 쿼리 재작성</h2>
<p>사용자 질문이 애매하거나 여러 주제를 섞어서 묻는 경우,
그대로 검색하면 정확한 결과를 못 찾는 경우가 많아요.</p>
<h2 id="💡-해결-방법-1">💡 해결 방법:</h2>
<p>Step-Back Prompting: 질문을 더 일반화해서 검색</p>
<p>HyDE: 질문으로 가상 문서를 생성 → 임베딩 후 검색</p>
<p>Query2Doc: LLM이 문서처럼 재작성해서 검색 효율 ↑</p>
<p>ITER-RETGEN: 생성과 검색을 반복해서 더 정확한 정보 획득</p>
<p>🔗 문맥 확장
하나의 문장만 검색되면 이해하기 어렵죠.
그래서 앞뒤 문장을 함께 가져오거나, 상위 문서 단위로 묶어서 전달해요.</p>
<p>Sentence Window Retrieval: 앞뒤 k개 문장 함께 전송</p>
<p>Parent Chunking: 청크들을 상위 의미 단위로 묶기</p>
<h2 id="🔎-2단계-retrieval-검색-최적화">🔎 2단계: Retrieval (검색 최적화)</h2>
<p>🧬 하이브리드 검색
키워드 검색 (BM25 등) → 정확한 단어 일치</p>
<p>의미 기반 검색 (벡터 임베딩) → 유사한 문맥 이해</p>
<p>💡 두 가지를 <strong>RRF (Reciprocal Rank Fusion)</strong>으로 조합해서
정확도와 커버리지를 동시에 높입니다.</p>
<h2 id="✂️-3단계-post-retrieval-결과-정리">✂️ 3단계: Post-Retrieval (결과 정리)</h2>
<p>🔄 리랭킹 (Re-Ranking)
검색된 Top-K 청크 중에서도
진짜 중요한 걸 위로 올려주는 과정입니다.</p>
<p>bge-reranker, Cohere API</p>
<p>RankGPT: LLM 기반으로 슬라이딩 윈도우 방식 리랭킹</p>
<h2 id="📉-프롬프트-압축-prompt-compression">📉 프롬프트 압축 (Prompt Compression)</h2>
<p>LLM에게 넣을 수 있는 토큰 수는 한계가 있어요.
(예: GPT-3.5는 약 4,000 tokens)</p>
<h2 id="💡-해결-방법-2">💡 해결 방법:</h2>
<p>Selective Context: 정보량 많은 부분만 남김</p>
<p>LLMLingua, LongLLMLingua: 의미 단위로 압축</p>
<p>AutoCompressor: soft prompt로 요약 정보 전달</p>
<p>RECOMP: 문장 단위로 압축 or 요약 생성</p>
<p>🧼 필터링
필요 없는 문서, 중복된 내용, 신뢰도 낮은 정보는 제거합니다.</p>
<p>FiD-TF, Self-RAG, CRAG 등은
토큰 수준에서 중요성 판단 후 필터링까지 수행해요!</p>
<h2 id="🧠-4단계-생성-generation">🧠 4단계: 생성 (Generation)</h2>
<p>다양한 생성 기술들
DSP: 여러 쿼리 → 여러 문서 검색 → 종합해서 응답 생성</p>
<p>PRCA: 보상 기반 학습으로 더 나은 응답 유도</p>
<p>REPLUG: 검색 결과를 그대로 LLM 입력에 추가</p>
<p>RECITE: 다양한 답변 생성 후 다수결로 최종 응답 선택</p>
<h2 id="🧑💻-기타-고급-구성">🧑‍💻 기타 고급 구성</h2>
<p>🗨️ Chat Engine
대화형에서는 이전 대화 내용을 자동 요약해서 쿼리에 반영해야 해요.
예: ContextChatEngine, CondensePlusContextMode</p>
<h2 id="🤖-agent-구조">🤖 Agent 구조</h2>
<p>문서마다 요약/질의응답 전담 Agent를 만들고,
Top Agent가 전체 통제 → 질문을 각 Agent에 전달하고 응답을 종합!</p>
<h2 id="🔧-모델-튜닝">🔧 모델 튜닝</h2>
<p>GPT-4로 질문-응답 데이터 만들고 → GPT-3.5에 파인튜닝</p>
<p>RA-DIT: Retriever + Generator를 동시에 학습시켜 성능 ↑</p>
<h2 id="✅-평가도-중요해요">✅ 평가도 중요해요!</h2>
<p>잘 작동하는 RAG인지 어떻게 알 수 있을까요?</p>
<ol>
<li><p>Ragas: 정답성, 검색 정확도, 응답 적절성 측정</p>
</li>
<li><p>LangSmith: 체인 내 행동 추적</p>
</li>
<li><p>OpenAI Cookbook: 평가 기준별 테스트 스크립트 제공</p>
</li>
</ol>
<p>참고자료: [ <a href="https://medium.com/@vipra_singh/building-llm-applications-advanced-rag-part-10-ec0fe735aeb1">https://medium.com/@vipra_singh/building-llm-applications-advanced-rag-part-10-ec0fe735aeb1</a> ]</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[<Ink&Codes>Bringing Museums to Life: Starting Our Journey with LLM, RAG, and Smart Glasses ]]></title>
            <link>https://velog.io/@yooni1231__/InkCodesBringing-Museums-to-Life-Starting-Our-Journey-with-LLM-RAG-and-Smart-Glasses</link>
            <guid>https://velog.io/@yooni1231__/InkCodesBringing-Museums-to-Life-Starting-Our-Journey-with-LLM-RAG-and-Smart-Glasses</guid>
            <pubDate>Wed, 02 Apr 2025 07:41:12 GMT</pubDate>
            <description><![CDATA[<p>Imagine walking through a museum with nothing but a pair of smart glasses—and having a personal AI docent narrate the story behind every piece of art, tailored just for you. No need to scan QR codes, fumble with audio guides, or wait for a human tour.</p>
<p>This is the future we&#39;re building.</p>
<p>We&#39;re kicking off an exciting project that combines the power of Large Language Models (LLMs), Retrieval-Augmented Generation (RAG), and wearable smart glasses to create an intelligent, context-aware museum guide. Our goal? To transform static exhibitions into deeply personalized, immersive storytelling experiences.</p>
<p>In this technical blog series, we&#39;ll be documenting our journey step by step—from early experiments to real-world implementation. We’ll dive into our architecture, data pipelines, model tuning, challenges, and everything in between.</p>
<p>If you&#39;re interested in conversational AI, edge computing, or just love museums and emerging tech, you’re in the right place.</p>
<p>Let’s begin.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Database week 2]]></title>
            <link>https://velog.io/@yooni1231__/Database-week-2</link>
            <guid>https://velog.io/@yooni1231__/Database-week-2</guid>
            <pubDate>Sun, 06 Oct 2024 18:53:03 GMT</pubDate>
            <description><![CDATA[<h4 id="raid-이란-redundant-array-of-independent-disk">RAID 이란? (Redundant Array of Independent Disk)</h4>
<p>2개 이상의 디스크를 병렬로 처리하여 성능 및 안전성을 향상시키는 방식
데이터 손실 방지를 위한 대비책 </p>
<p>레이드 종류</p>
<p> &amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp레이드 0         레이드 1     레이드 2     &amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp    레이드 3    </p>
<p> parity bits &amp;nbsp&amp;nbsp&amp;nbsp    x            &amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp    x        &amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsperror corr.     &amp;nbsp&amp;nbsp&amp;nbsp&amp;nbspdedicateddisk
 mirroring    &amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp    x            &amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp    0
 striping     &amp;nbsp&amp;nbsp&amp;nbsp    block level    &amp;nbsp&amp;nbsp    x    &amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp    bit        &amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp    byte
     &amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp</p>
<p>레이드 0 데이터 분산 처리 
레이드 1 데이터 복제
레이드 2 에러 체크와 수정을 할 수 있도록 해밍 코드 사용 
페리티 정보 디스크 별도 저장 레이드 3  바이트 단위 데이터 저장
                         레이드 4 블록 단위 데이터 저장 </p>
<h4 id="select-execution">Select Execution</h4>
<p>Q1. How does the DBMS find the pieces of Data on Disk?</p>
<pre><code>            Logical           Physical                                                                        </code></pre><p>Schema        &amp;nbsp&amp;nbsp    Database<br>                &amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp<span style="color: red">Tablespace</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: red">DataFile</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                    Segment&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: red">Extent</span><br>                &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Oracle data block&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OS block </p>
<p><img src="https://velog.velcdn.com/images/yooni1231__/post/e4b8f490-d810-44ce-a3b7-d1d0d1e34e07/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yooni1231__/post/f7994a29-49dc-4f9c-bf27-3b28432a54bc/image.png" alt=""></p>
<h4 id="fixed-length-records-formats-vs-variable-length-record-fomats">Fixed length records formats vs Variable length record fomats</h4>
<p>Fixed-length records fomats: fileds stored consecutively
Varialbe-length record formats: array of offsets, null values when start offset = end offset</p>
<h4 id="file-structure">File Structure</h4>
<p>Data Items -&gt; Records -&gt; Blocks -&gt; Files -&gt; Memory </p>
<p>Records: Collection of related data items (fields) </p>
<p>Records into blocks </p>
<p><span style="color:red">&lt;4 options&gt;</p>
<ol>
<li>Separating records
fixed size recs are not need to separate
special marker
give record lengths ( or offsets  / within each record, in block header</li>
</ol>
<ol start="2">
<li><p>Spanned vs Unspanned<br>Spanned &amp;nbsp &amp;nbsp&amp;nbsp&amp;nbsp   need indication of partial record/continuation
if record size &gt; block size </p>
<p>Unspanned &amp;nbsp&amp;nbsp records within one block </p>
</li>
</ol>
<ol start="3">
<li>Sequencing
ordering records in file ( and block ) by some key value 
read records efficiently</li>
</ol>
<p>  <span style="color:red">&lt;3 options&gt;</span>
  #1 next record physically contiguous
  #2 linked
  #3 overflow area 
4. Indirection 
    완전 물리적(fully physical):
  physical&lt;-&gt;indirect
물리적 파일 구조는 데이터를 직접적으로 가리킴
파일을 구성하는 데이터 블록들이 물리적 주소나 위치에 바로 매핑
    완전 간접(fully indirect):
모든 데이터가 간접적으로 참조<br>하나의 인덱스 블록이 다른 인덱스 블록을 가리키고, 그 인덱스 블록이 실제 데이터를 가리키는 방식
매우 큰 파일을 처리할 때 사용되며, 계층적인 구조를 통해 데이터를 관리</p>
<p> Block header - data at beginning that describes block</p>
<h4 id=""></h4>
<pre><code>Advantages of Column Store</code></pre><p>more compact storage
efficient reads on data mining operations</p>
<pre><code>Advantages of Row Store</code></pre><p>writes (multiple fields of one record) more efficient
efficient reads for record access (OLTP)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[LeetCode- Add Two numbers ]]></title>
            <link>https://velog.io/@yooni1231__/LeetCode-Add-Two-numbers</link>
            <guid>https://velog.io/@yooni1231__/LeetCode-Add-Two-numbers</guid>
            <pubDate>Sat, 03 Aug 2024 21:46:02 GMT</pubDate>
            <description><![CDATA[<p>LeetCode&#39;s &quot;Add Two Numbers&quot; problem is a popular algorith challenge that involves adding two non-negative integers represented by linked lists. Each linked list stores digit in reverse order, with each node containing a single digit. Our goal is to add the two numbers and return the sum as a linked list.</p>
<h4 id="problem-descriptoin">Problem Descriptoin</h4>
<p>Given two non-empty linked lists representing two non-negative integers, the digits are stored in reverse order, and each node contains a single digit. Add the two numbers and return the result as a linked list. You may assume the two numbers do not contain any leading zero, except the number 0 itself. </p>
<h4 id="exmaple">Exmaple</h4>
<pre><code>(2 -&gt; 4 -&gt; 3) + (5 -&gt; 6 -&gt; 4)
Output: 7 -&gt; 0 -&gt; 8
Explanation: 342 + 465 = 807
</code></pre><h4 id="approach">Approach</h4>
<p>To solve this problem, we will use a dummy node to simplify the edge cases handling and iterate through both linked lists to compute the sum digit by digit while managing the carry-over value.</p>
<h4 id="solution">Solution</h4>
<p><strong>ListNode Class</strong>
Each node contains a value &#39;val&#39; and a reference to the next node &#39;next&#39;
This code is given. </p>
<pre><code>class ListNode {
    int val;
    ListNode next;
    ListNode() {}
    ListNode(int val) { this.val = val; }
    ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}</code></pre><p><strong>addTwoNumbers Method</strong>
Dummy Node: We use dummy node to simplify the code for edge cases, such as when the list is empty. The &#39;dummy&#39;node serves as the starting point of the result list.</p>
<p>Current List: The &#39;current&#39; node is used to build the result list. Initially, it points to the dummy node.</p>
<p>Carry Variable: The &#39;carry&#39; variable holds any carry-over value from the sum of two digits.</p>
<pre><code>public class AddTwoNumbersSolution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode(0);  // Dummy node to hold the result list
        ListNode current = dummy;
        int carry = 0;  // Variable to store the carry value
</code></pre><p><strong>While Loop</strong>
This loop continues until both linked lists are fully traversed and there is no carry left.</p>
<p>Value Extraction: For each node in &#39;l1&#39;and &#39;l2&#39;, we extract the value if the node is not null; otherwise, we use 0</p>
<p>Sum Calculation: We calculate the sum of the extracted values and the carry.</p>
<p>Carry Update. The carry is updated to &#39;total/10&#39;</p>
<p>New Node Creation: We create a new node with the value &#39;total%10&#39; and link it to the current node.</p>
<p>Advance Nodes: We move the &#39;l1&#39;,&#39;l2&#39;and &#39;current&#39; pointers to their respective next nodes.</p>
<h4 id="conclusion">Conclusion</h4>
<p>This  solution efficiently adds two numbers represented by linked lists in reverse order. By leveraging a dummy node and handling the carry-over value, the implementation is both clean and easy to understand. This approach ensures that we can handle any edge cases gracefully.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Leetcode - Two Sum problem in java ]]></title>
            <link>https://velog.io/@yooni1231__/Leetcode-Two-Sum-problem-in-java</link>
            <guid>https://velog.io/@yooni1231__/Leetcode-Two-Sum-problem-in-java</guid>
            <pubDate>Thu, 25 Jul 2024 22:04:07 GMT</pubDate>
            <description><![CDATA[<p>The Two Sum problem from leetcode is a algorithm challenge where you need to find two numbers in an arraythat add up to a specific target. </p>
<p>*<em>Problem Statement 
*</em>
Given an array of integers &#39;nums&#39; and an integer &#39;target&#39;, return the indices of the two numbers such that tey add up to &#39;target&#39;.
You may not use the same element twice. </p>
<p>*<em>Example 
*</em></p>
<pre><code>Input: nums = [2, 7, 11, 15], target = 9
Output: [0, 1]
Explanation: Because nums[0] + nums[1] == 9, we return [0, 1].
</code></pre><p>*<em>Initial Approach *</em></p>
<h4 id="solution-1-brute-force">Solution 1. Brute Force</h4>
<pre><code>
class Solution {
    public int[] twoSum(int[] nums, int target) {
        // Loop through each element
        for (int i = 0; i &lt; nums.length; i++) {
            // Loop through each element after the current element
            for (int j = i + 1; j &lt; nums.length; j++) {
                // Check if the sum of the two elements equals the target
                if (nums[i] + nums[j] == target) {
                    // Return the indices of the two elements
                    return new int[] {i, j};
                }
            }
        }
        // If no solution is found, throw an exception
        throw new IllegalArgumentException(&quot;No two sum solution&quot;);
    }
}
</code></pre><p>#1. loop through each element: The outer loop iterates through each element in the array starting from the first element.
#2. loop through each subsequent element: The inner loop interates through the elements that come after the current element in the outer loop.
#3. check if the sum equals the target: inside the inner loop, we check if the sum of the elements at indices &#39;i&#39; and &#39;j&#39; equals the target. 
#4. return the indices. If the sum equals the target, we return the indieces &#39;[i,j]&#39;.
#5. Handle no solution case: If no pair is found that sums to the target, we throw an &#39;IllegalArgumentException&#39;.</p>
<p>*<em>Complexity Analysis
*</em></p>
<ul>
<li>Time complexity 0(N^2) </li>
<li>Space complexity 0(1) </li>
</ul>
<h3 id="solution-2-hash-map-">*<em>Solution 2. Hash Map *</em></h3>
<p>Using Hash Map is a more efficient solution using a HashMap. </p>
<h4 id="---what-is-hash-map--">-- What is Hash Map?--</h4>
<p>A &#39;HashMap&#39; is a part of Java&#39;s Collection Framework and provides a way to store key-value pairs. It is often implementation of the &#39;Map&#39; interface and is often used for its fast retrieval capabilities.</p>
<p>⭐key characteristics of** HashMap**⭐</p>
<ol>
<li>unordered collectoin</li>
<li>allows null values</li>
<li>non-sysnchronized  ( you should use &#39;Collections.synchronizedMap()&#39; </li>
</ol>
<p>🌟Solution using HashMap🌟</p>
<ol>
<li>Store Complement 
As we iterate through the array, we calculate the complement(i.e.. &#39;target-nums[i])</li>
<li>Check Complement 
Before adding the current element to the map, we check if its complement is already present in the map.</li>
<li>Return Indices.
If the complement exists, we return the indices of the complement and the current element. </li>
</ol>
<pre><code>import java.util.HashMap;
import java.util.Map;

class Solution{
    public int[] twoSum(int[] nums, int target){
    //HashMap to store the number and its index
    Map&lt;Integer,Integer&gt; map = new HashMap&lt;&gt;();

    //Iterate through the array
    for (int i=0; i&lt;nums.length; i++){
    int complemet = target-nums[i]
    //check if the complement is already in the map 

    if (map.containsKey(complement)){
    //if found, return the indices of the complement and current number
    return new int[] { map.get(complement),i};
    //add the current number and its index to the map
    map.put(nums[i],i); 

    //if no solution is found, throw an exception 
    throw new IllegalArgumentException(&quot;No two sum solution&quot;); 
    }
    }
</code></pre>]]></description>
        </item>
    </channel>
</rss>