<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Growth Diary</title>
    <link>https://striver.tistory.com/</link>
    <description>개발에 관련된 다양한 IT 지식을 공유하고 최신 트렌드 기술을 배워나갑니다.</description>
    <language>ko</language>
    <pubDate>Tue, 30 Jun 2026 21:08:37 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>dong_seok</managingEditor>
    <image>
      <title>Growth Diary</title>
      <url>https://tistory1.daumcdn.net/tistory/5368264/attach/48ffa397b0d34d4e8fca6dc51a6230fb</url>
      <link>https://striver.tistory.com</link>
    </image>
    <item>
      <title>[OSSCA] 파이토치 문서 한글화 1,2주차</title>
      <link>https://striver.tistory.com/entry/pytorch-1%EC%A3%BC%EC%B0%A8</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;OSSCA(오픈소스 컨트리뷰션 아카데미)에서 주최하는 멘토링 프로그램에 2지망으로 신청했던 파이토치 문서 한글화 멘티로 선정되었습니다. 오픈소스 경험이 없던터라 실제 오픈소스 프로젝트에 기여해볼 수 있는 좋은 기회라고 판단해서 적극적으로 참여하기로 했습니다. 이 글에서는 1~2주차 과제를 수행하며 배운 점과 실제 기여 과정을 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. OSSCA 파이토치 한글화 멘토링이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OSSCA는 국내 개발자들이 오픈소스 프로젝트에 직접 기여할 수 있도록 멘토와 멘티를 매칭해주는 프로그램입니다. 파이토치 한글화 트랙은 PyTorch 공식 튜토리얼(&lt;a href=&quot;https://github.com/pytorch/tutorials&quot;&gt;pytorch/tutorials&lt;/a&gt;)을 한국어로 번역하는 &lt;a href=&quot;https://github.com/PyTorchKorea/tutorials-kr&quot;&gt;PyTorchKorea/tutorials-kr&lt;/a&gt; 프로젝트에 기여합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6주간 멘토의 리뷰를 받으며 번역 품질을 높이는 방식으로 진행되며, 오탈자 수정&amp;middot;오번역 개선&amp;middot;용어집 정비 등 다양한 형태의 기여가 가능합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 1주차: 번역 원칙 이해와 첫 PR 연습&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1. 필독 리스트에서 정리한 번역 중요점 3가지&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1주차 과제의 첫 번째 단계는 TRANSLATION_GUIDE.md, CONTRIBUTING.md, 번역 모범 사례(javascript.info)를 읽고 핵심을 정리하는 것이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 문서를 읽으며 가장 중요하다고 느낀 점을 세 가지로 추렸습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;중요 원칙&lt;/th&gt;
&lt;th&gt;이유&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;b&gt;의역&amp;middot;설명 추가 시 원문 의미 변질 금지&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;직역이 어색해도 원문 의도를 바꾸면 오히려 독자에게 잘못된 정보를 전달한다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;b&gt;번역 후 기호 검색으로 문법 오류 검토&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;RST 문서 특성상 &lt;code&gt;`&lt;/code&gt;&amp;middot;&lt;code&gt;:math:&lt;/code&gt; 등 기호 오류가 빌드 실패로 이어지기 쉽다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;b&gt;기여 전 기존 이슈/PR 선행 확인&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;이미 논의 중이거나 종결된 이슈에 중복 작업을 하면 낭비가 된다&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2. 연습 레포에서 이슈 생성 및 PR 제출&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필독을 마친 뒤, 실전 흐름을 익히기 위해 연습용 레포(&lt;a href=&quot;https://github.com/jih0-kim/2026-OSSCA-test&quot;&gt;jih0-kim/2026-OSSCA-test&lt;/a&gt;)에서 3~4줄 분량의 번역 PR을 올렸습니다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;# 로컬 빌드로 번역 결과 확인
python -m venv .venv
source .venv/bin/activate
pip install -r .build/requirements-minimal.txt
make html-noplot
open _build/html/index.html&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드가 정상적으로 완료된 것을 확인한 뒤 PR을 올렸습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이슈: &lt;a href=&quot;https://github.com/jih0-kim/2026-OSSCA-test/issues/42&quot;&gt;https://github.com/jih0-kim/2026-OSSCA-test/issues/42&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;PR: &lt;a href=&quot;https://github.com/jih0-kim/2026-OSSCA-test/pull/43&quot;&gt;https://github.com/jih0-kim/2026-OSSCA-test/pull/43&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 2주차 ①: 오번역 수정&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1. 발견한 오번역&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;intermediate_source/autograd_saved_tensors_hooks_tutorial.py&lt;/code&gt; 파일을 검토하던 중, &lt;code&gt;gradient&lt;/code&gt;를 &lt;b&gt;기울기&lt;/b&gt;로 번역한 부분을 발견했습니다. TRANSLATION_GUIDE.md에는 &lt;code&gt;gradient&lt;/code&gt;의 공식 번역어가 명확히 &lt;b&gt;변화도&lt;/b&gt;로 명시되어 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnXsf6/dJMcaakRGQp/FEb0hQSPmqdhJ77Uo2KqTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnXsf6/dJMcaakRGQp/FEb0hQSPmqdhJ77Uo2KqTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnXsf6/dJMcaakRGQp/FEb0hQSPmqdhJ77Uo2KqTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnXsf6%2FdJMcaakRGQp%2FFEb0hQSPmqdhJ77Uo2KqTk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1392&quot; height=&quot;312&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;312&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 공식 가이드를 기준으로 수정 후 PR을 작성하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;수정 전&lt;/th&gt;
&lt;th&gt;수정 후&lt;/th&gt;
&lt;th&gt;근거&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;기울기&lt;/td&gt;
&lt;td&gt;변화도&lt;/td&gt;
&lt;td&gt;TRANSLATION_GUIDE.md: &lt;code&gt;gradient &amp;rarr; 변화도&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 수정 전 원문 코드 일부입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1102&quot; data-origin-height=&quot;527&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btsy37/dJMcaiwpFeo/Xi3bLAC5wg4F9bhfSHye50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btsy37/dJMcaiwpFeo/Xi3bLAC5wg4F9bhfSHye50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btsy37/dJMcaiwpFeo/Xi3bLAC5wg4F9bhfSHye50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbtsy37%2FdJMcaiwpFeo%2FXi3bLAC5wg4F9bhfSHye50%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1102&quot; height=&quot;527&quot; data-origin-width=&quot;1102&quot; data-origin-height=&quot;527&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;!-- 이미지 1: 수정 전 — gradient가 '기울기'로 번역된 화면 --&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2. 수정 과정과 PR&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오번역 위치를 파악한 뒤 이슈를 먼저 등록하고, 수정 후 PR을 제출했습니다.&lt;/p&gt;
&lt;!-- 이미지 2: 수정 후 — '변화도'로 교체된 화면 --&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEq88G/dJMcaiQHKPl/ENfrh2ziiqMtmAHyCNPb60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEq88G/dJMcaiQHKPl/ENfrh2ziiqMtmAHyCNPb60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEq88G/dJMcaiQHKPl/ENfrh2ziiqMtmAHyCNPb60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEq88G%2FdJMcaiQHKPl%2FENfrh2ziiqMtmAHyCNPb60%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;308&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;308&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;!-- 이미지 3: GitHub PR 화면 --&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이슈: &lt;a href=&quot;https://github.com/PyTorchKorea/tutorials-kr/issues/1092&quot;&gt;https://github.com/PyTorchKorea/tutorials-kr/issues/1092&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;PR: &lt;a href=&quot;https://github.com/PyTorchKorea/tutorials-kr/pull/1093&quot;&gt;https://github.com/PyTorchKorea/tutorials-kr/pull/1093&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 2주차 ②: 용어집에 새 용어 추가&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1. 추가 배경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;번역 작업을 진행하다 보면 용어집에 등재되지 않은 단어가 종종 등장합니다. &lt;code&gt;Subclass&lt;/code&gt;도 그 중 하나였습니다. 번역자마다 다르게 옮길 수 있는 단어인 만큼, 공식 용어집에 추가해 일관성을 확보하는 것이 바람직하다고 판단했습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;영문&lt;/th&gt;
&lt;th&gt;한글&lt;/th&gt;
&lt;th&gt;비고&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Subclass&lt;/td&gt;
&lt;td&gt;상속&lt;/td&gt;
&lt;td&gt;신규 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.2. 이슈 &amp;rarr; PR 흐름&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;용어 추가도 코드 수정과 동일하게 이슈 &amp;rarr; PR 순서로 진행했습니다. 용어 선정의 근거를 이슈에 먼저 서술해 리뷰어가 맥락을 파악할 수 있도록 했습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이슈: &lt;a href=&quot;https://github.com/PyTorchKorea/tutorials-kr/issues/1094&quot;&gt;https://github.com/PyTorchKorea/tutorials-kr/issues/1094&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;PR: &lt;a href=&quot;https://github.com/PyTorchKorea/tutorials-kr/pull/1095&quot;&gt;https://github.com/PyTorchKorea/tutorials-kr/pull/1095&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 이 용어집 추가의 경우, 멘토분들이나 다른 기여자 분들의 검토 및 동의하에 최종적으로 PR이 진행 될 예정이라고하셔서, 의견제시만 했을뿐 용어집에 추가가 되지는 않을 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 1~2주차를 돌아보며 &amp;mdash; 오픈소스 기여에서 얻은 것&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2주간의 활동을 통해 &quot;좋은 번역 규칙&quot;과 &quot;올바른 오픈소스 기여 프로세스&quot;를 배울 수 있었습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;width: 103px;&quot;&gt;영역&lt;/th&gt;
&lt;th style=&quot;width: 388px;&quot;&gt;배운 점&lt;/th&gt;
&lt;th style=&quot;width: 329px;&quot;&gt;적용 방법&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 103px;&quot;&gt;&lt;b&gt;번역 품질&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 388px;&quot;&gt;번역 가이드 규칙이나 용어집 한 단어 차이가 문서 전체 일관성에 영향&lt;/td&gt;
&lt;td style=&quot;width: 329px;&quot;&gt;기여 전 TRANSLATION_GUIDE.md 재확인 습관화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 103px;&quot;&gt;&lt;b&gt;프로세스&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 388px;&quot;&gt;이슈 선등록 &amp;rarr; PR 제출 흐름이 리뷰 효율을 높임&lt;/td&gt;
&lt;td style=&quot;width: 329px;&quot;&gt;수정이 작더라도 이슈 없이 PR 직행하지 않기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 103px;&quot;&gt;&lt;b&gt;중복 방지&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 388px;&quot;&gt;기존 이슈/PR 확인 없이 시작하면 헛수고&lt;/td&gt;
&lt;td style=&quot;width: 329px;&quot;&gt;키워드 검색으로 선행 논의 유무 먼저 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3~4주차에는 더 긴 분량의 번역 개선을 진행할 예정입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참고자료&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/PyTorchKorea/tutorials-kr/blob/master/TRANSLATION_GUIDE.md&quot;&gt;https://github.com/PyTorchKorea/tutorials-kr/blob/master/TRANSLATION_GUIDE.md&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/PyTorchKorea/tutorials-kr/blob/master/CONTRIBUTING.md&quot;&gt;https://github.com/PyTorchKorea/tutorials-kr/blob/master/CONTRIBUTING.md&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/javascript-tutorial/ko.javascript.info/wiki/%EB%B2%88%EC%97%AD-%EB%AA%A8%EB%B2%94-%EC%82%AC%EB%A1%80&quot;&gt;https://github.com/javascript-tutorial/ko.javascript.info/wiki/%EB%B2%88%EC%97%AD-%EB%AA%A8%EB%B2%94-%EC%82%AC%EB%A1%80&lt;/a&gt;&lt;/p&gt;</description>
      <category>Activities/Open source</category>
      <category>OpenSource</category>
      <category>OSSCA</category>
      <category>pytorch</category>
      <category>pytorchkorea</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/116</guid>
      <comments>https://striver.tistory.com/entry/pytorch-1%EC%A3%BC%EC%B0%A8#entry116comment</comments>
      <pubDate>Sat, 2 May 2026 23:24:55 +0900</pubDate>
    </item>
    <item>
      <title>[Claude] Claude code 기초 체험</title>
      <link>https://striver.tistory.com/entry/Claude-Claude-code-%EA%B8%B0%EC%B4%88-%EC%B2%B4%ED%97%98</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;평소 Claude Code를 터미널에서 실행해 소스 코드 관련 Q&amp;amp;A 용도로만 가볍게 사용해왔습니다. 그러다 최근 Claude Code가 생각보다 훨씬 다양한 기능을 제공한다는 사실을 알게 되었고, 이를 계기로 관련 기능들을 정리하고 직접 활용해보는 시간을 가졌습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. Claude Code 기본 명령어 정리&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본격적인 설정에 앞서 Claude Code의 핵심 명령어를 정리했습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1.1. 터미널 실행 명령어&lt;/b&gt;&lt;/h4&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;명령어&lt;/td&gt;
&lt;td&gt;기능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;claude&lt;/td&gt;
&lt;td&gt;Claude Code 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;claude --dangerously-skip-permissions&lt;/td&gt;
&lt;td&gt;Auto Mode로 실행 (권한 확인 생략)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1.2. Claude Code 내부 CLI 명령어&lt;/b&gt;&lt;/h4&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;명령어&lt;/td&gt;
&lt;td&gt;기능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/ide&lt;/td&gt;
&lt;td&gt;IDE 연동 (Diff viewer, 라인 선택)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/model&lt;/td&gt;
&lt;td&gt;사용할 모델 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@&lt;/td&gt;
&lt;td&gt;파일 첨부&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;shift + tab / alt + m (윈도우)&lt;/td&gt;
&lt;td&gt;Plan Mode &amp;harr; Auto Mode 전환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/resume&lt;/td&gt;
&lt;td&gt;이전 작업 이어서 진행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/memory&lt;/td&gt;
&lt;td&gt;프로젝트/유저 메모리 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;#&lt;/td&gt;
&lt;td&gt;메모리와 유사한 기능 (작동 방식 상이)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/init&lt;/td&gt;
&lt;td&gt;Claude.md 파일 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/mcp&lt;/td&gt;
&lt;td&gt;MCP 서버 목록 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/compact&lt;/td&gt;
&lt;td&gt;대화 기록 요약 (기존 대화 삭제)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/clear&lt;/td&gt;
&lt;td&gt;기존 대화 내역 전체 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/permissions&lt;/td&gt;
&lt;td&gt;사용 가능한 도구 권한 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/exit&lt;/td&gt;
&lt;td&gt;Claude Code 종료 (Ctrl+C 2번이 더 빠름)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/usage&lt;/td&gt;
&lt;td&gt;현재 세션과 금주 세션 Claude 사용량 모니터링&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/plugins&lt;/td&gt;
&lt;td&gt;사용 가능하거나 설치된 플러그인 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 본격적으로 GitHub MCP 설정 과정을 살펴보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 첫 번째 시도: User Scope 플러그인 설치&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.1. 플러그인 설치&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code에서 GitHub 플러그인을 설치하는 것은 간단합니다:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;bash&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;python&quot; style=&quot;color: #383a42; text-align: left;&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# Claude Code 실행
claude

# 플러그인 설치
/plugin
# Discover 탭에서 github 검색 후 설치
```

### 2.2. 로컬 작업은 성공

처음에는 순조로웠습니다. Claude에게 &quot;Git 저장소를 초기화하고 커밋해줘&quot;라고 요청하니, 로컬 Git 작업은 완벽하게 수행되었습니다:

- ✅ `git init` 실행
- ✅ `.gitignore` 파일 생성
- ✅ 파일 스테이징 및 커밋
- ✅ 브랜치 `main`으로 설정

### 2.3. 문제 발생: 원격 저장소 연동 실패

하지만 &quot;GitHub에 새 저장소를 만들고 Push해줘&quot;라고 요청하자 문제가 발생했습니다:
```
❌ MCP 연결 끊김
❌ Invalid MCP server config for 'github': Missing environment variables: GITHUB_PERSONAL_ACCESS_TOKEN&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원인을 분석한 결과, &lt;b&gt;GitHub Personal Access Token이 설정되지 않아서&lt;/b&gt; MCP 서버가 GitHub API와 통신할 수 없었던 것입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 문제 진단과 해결 과정&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3.1. MCP 서버 상태 확인&lt;/b&gt;&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;bash&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;groovy&quot; style=&quot;color: #383a42; text-align: left;&quot;&gt;&lt;code&gt;claude mcp list
```

실행 결과:
```
plugin:github:github: https://api.githubcopilot.com/mcp/ (HTTP) - ✗ Failed to connect
Status: ✘ not authenticated&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3.2. 근본 원인 파악 및 해결 방향 결정&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code의 안내 메시지를 보니 핵심 문제가 명확했습니다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;User Scope 플러그인&lt;/b&gt;은 전역 설정(환경변수)을 필요로 함&lt;/li&gt;
&lt;li&gt;프로젝트별 .mcp.json 파일로는 User Scope 플러그인 설정 불가&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GitHub Personal Access Token&lt;/b&gt; 미설정&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 에러를 해결하지 못해서&amp;nbsp;&lt;b&gt;표준 MCP 서버를 직접 설정&lt;/b&gt;하는 방식으로 방향을 전환했습니다. 플러그인으로 해보고싶었지만, 표준 MCP 서버를 직접 설정해보는 방식해도 해보지 못했기에, 가능한 방법을 먼저 진행해보고 해결하지못한 이슈는 추후에 다시 시도하기로 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4. 해결책: 표준 MCP 서버 직접 설정&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4.1. Project Scope로 MCP 서버 설정&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플러그인 대신, 프로젝트별로 MCP 서버를 직접 설정하기로 결정했습니다. 이 방식은 여러 프로젝트에서 다른 GitHub 계정을 사용할 수 있다는 장점도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4.2. GitHub Personal Access Token 발급&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;GitHub 웹사이트 접속: &lt;a href=&quot;https://github.com/settings/tokens&quot;&gt;https://github.com/settings/tokens&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&quot;Generate new token (classic)&quot;&lt;/b&gt; 클릭&lt;/li&gt;
&lt;li&gt;필요한 권한 선택:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;✅ repo (전체 저장소 접근 권한)&lt;/li&gt;
&lt;li&gt;✅ workflow (GitHub Actions 워크플로우)&lt;/li&gt;
&lt;li&gt;✅ delete_repo (저장소 삭제, 선택사항)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&quot;Generate token&quot;&lt;/b&gt; 클릭&lt;/li&gt;
&lt;li&gt;생성된 토큰 복사 (형식: ghp_xxxxxxxxxxxxxxxxxxxxx)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⚠️ &lt;b&gt;주의&lt;/b&gt;: 토큰은 생성 직후에만 확인 가능하므로 안전한 곳에 보관이 필요합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4.3. .mcp.json 파일 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 루트에 .mcp.json 파일을 직접 생성하고 토큰을 설정합니다:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;bash&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;python&quot; style=&quot;color: #383a42; text-align: left;&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;cd /Users/dongseok/Desktop/workspace/프로젝트명/test

# .mcp.json 파일 생성
cat &amp;gt; .mcp.json
{
  &quot;mcpServers&quot;: {
    &quot;github&quot;: {
      &quot;command&quot;: &quot;npx&quot;,
      &quot;args&quot;: [&quot;-y&quot;, &quot;@modelcontextprotocol/server-github&quot;],
      &quot;env&quot;: {
        &quot;GITHUB_PERSONAL_ACCESS_TOKEN&quot;: &quot;ghp_여기에_실제_발급받은_토큰_입력&quot;
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4.4. 보안 설정: .gitignore 업데이트&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토큰이 포함된 .mcp.json 파일이 GitHub에 업로드되지 않도록 .gitignore에 추가합니다:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;bash&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;css&quot; style=&quot;color: #383a42; text-align: left;&quot;&gt;&lt;code&gt;echo &quot;.mcp.json&quot; &amp;gt;&amp;gt; .gitignore&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4.5. Claude Code 재시작 및 확인&lt;/b&gt;&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;bash&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;perl&quot; style=&quot;color: #383a42; text-align: left;&quot;&gt;&lt;code&gt;# Claude Code 재시작
# Ctrl+C로 종료 후
claude

# MCP 서버 상태 확인
claude mcp list
```

이제 결과를 보면:
```
plugin:github:github: ... - ✗ Failed to connect    # 플러그인은 여전히 실패
github: npx ... - ✓ Connected                       # 직접 설정한 MCP 서버 성공!&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;* 중요한 발견 : &lt;/b&gt;처음에는 플러그인 설정을 수정해서 성공한 줄 알았지만, 실제로는 &lt;b&gt;플러그인은 여전히 실패 상태&lt;/b&gt;였고, .mcp.json으로 설정한 &lt;b&gt;별도의 MCP 서버가 작동&lt;/b&gt;하고 있었던 것입니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4.6. 실패한 플러그인 제거 (선택사항)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혼란을 방지하기 위해 작동하지 않는 플러그인을 제거할 수 있습니다:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;bash&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;python&quot; style=&quot;color: #383a42; text-align: left;&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;/plugin uninstall github
```

제거 후에는 다음과 같이 깔끔하게 하나만 표시됩니다:
```
github: npx -y @modelcontextprotocol/server-github - ✓ Connected
```&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;5. 시행착오로 배운 핵심 포인트&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5.1. Scope 이해하기&lt;/b&gt;&lt;/h4&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Scope설정&lt;/td&gt;
&lt;td&gt;위치적용&lt;/td&gt;
&lt;td&gt;범위&lt;/td&gt;
&lt;td&gt;장점&lt;/td&gt;
&lt;td&gt;단점&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;User Scope&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;~/.zshrc 환경변수&lt;/td&gt;
&lt;td&gt;전역 (모든 프로젝트)&lt;/td&gt;
&lt;td&gt;한 번 설정으로 끝&lt;/td&gt;
&lt;td&gt;프로젝트별 다른 계정 사용 불가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Project Scope&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;.mcp.json&lt;/td&gt;
&lt;td&gt;프로젝트별&lt;/td&gt;
&lt;td&gt;계정/설정 분리 가능&lt;/td&gt;
&lt;td&gt;프로젝트마다 설정 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5.2. 파일 역할 구분&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;.claude&lt;/b&gt;: 플러그인 활성화 on/off만 관리 (자동 생성되었으나 실제로는 사용되지 않음)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;.mcp.json&lt;/b&gt;: MCP 서버 설정 및 환경변수 정의 (실제 작동하는 설정)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;.env&lt;/b&gt;: 환경변수 대안 (선택사항)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://principled-learning-2c9.notion.site/216dc93a597c80f6953ef4d048d55926&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://principled-learning-2c9.notion.site/216dc93a597c80f6953ef4d048d55926&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/AI 활용</category>
      <category>.mcp.json</category>
      <category>Claude</category>
      <category>claude code</category>
      <category>github</category>
      <category>github mcp</category>
      <category>MCP</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/115</guid>
      <comments>https://striver.tistory.com/entry/Claude-Claude-code-%EA%B8%B0%EC%B4%88-%EC%B2%B4%ED%97%98#entry115comment</comments>
      <pubDate>Sun, 25 Jan 2026 21:49:10 +0900</pubDate>
    </item>
    <item>
      <title>[GPU 기초] AI 시대를 지배하는 하드웨어, NVIDIA GPU의 모든 것</title>
      <link>https://striver.tistory.com/entry/CS-AI-%EC%8B%9C%EB%8C%80%EB%A5%BC-%EC%A7%80%EB%B0%B0%ED%95%98%EB%8A%94-%ED%95%98%EB%93%9C%EC%9B%A8%EC%96%B4-NVIDIA-GPU%EC%9D%98-%EB%AA%A8%EB%93%A0-%EA%B2%83</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;최근 AI 기술의 폭발적인 성장과 함께 하드웨어 인프라에 대한 관심이 뜨겁습니다. NVIDIA가 시장을 독점하는 이유와 아키텍처별 진화 과정, 그리고 규제 속에서 등장한 '800 시리즈'의 기술적 디테일을 완벽 정리해 드립니다.&lt;/p&gt;
&lt;hr data-path-to-node=&quot;5&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;6&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. AMD vs NVIDIA: 왜 모두가 NVIDIA를 선택할까?&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;하드웨어 스펙 시트상으로는 AMD도 강력한 경쟁자이지만, AI 실무 현장에서 NVIDIA가 압도적인 이유는 단순한 칩 성능 그 이상에 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;CUDA 생태계 (The Software Moat):&lt;/span&gt;&lt;/b&gt;&amp;nbsp;2006년부터 구축된 CUDA는 모든 AI 프레임워크와 완벽히 호환되며, 전 세계 개발자들이 가장 선호하는 표준입니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;연산 효율의 차이&lt;/span&gt;:&lt;/b&gt; NVIDIA는 일찍부터 AI 전용 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;30&quot; data-path-to-node=&quot;8,1,0&quot;&gt;Tensor Core&lt;/b&gt;&lt;/span&gt;를 도입하여 행렬 연산 속도를 비약적으로 높였습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,2,0&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;상호 연결 기술 (NVLink)&lt;/span&gt;:&lt;/b&gt; 수만 개의 GPU를 하나로 묶어 거대 모델(LLM)을 학습시킬 때 발생하는 데이터 병목 현상을 해결하는 핵심 무기입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-path-to-node=&quot;14&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;15&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. NVIDIA GPU 아키텍처의 진화와 성능 개발 내역&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-path-to-node=&quot;16&quot; data-ke-size=&quot;size16&quot;&gt;NVIDIA는 매 세대마다 SM 개수와 주기당 연산량을 늘리며 성능의 한계를 돌파해왔습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;17&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.1395%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;아키텍처&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.60465%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;대표 모델&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 10.2326%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;SM 개수&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 36.8605%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;주요 특징&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25.9302%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;AI 연산 성능&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.1395%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-path-to-node=&quot;17,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,1,0,0&quot;&gt;Volta&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.60465%;&quot;&gt;&lt;span data-path-to-node=&quot;17,1,1,0&quot;&gt;V100&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 10.2326%;&quot;&gt;&lt;span data-path-to-node=&quot;17,1,2,0&quot;&gt;80&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 36.8605%;&quot;&gt;&lt;span data-path-to-node=&quot;17,1,3,0&quot;&gt;최초의 AI 전용 텐서 코어 아키텍처&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25.9302%;&quot;&gt;&lt;span data-path-to-node=&quot;17,1,4,0&quot;&gt;125 TFLOPS (FP16)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.1395%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-path-to-node=&quot;17,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,2,0,0&quot;&gt;Turing&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.60465%;&quot;&gt;&lt;span data-path-to-node=&quot;17,2,1,0&quot;&gt;T4&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 10.2326%;&quot;&gt;&lt;span data-path-to-node=&quot;17,2,2,0&quot;&gt;40&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 36.8605%;&quot;&gt;&lt;span data-path-to-node=&quot;17,2,3,0&quot;&gt;추론용 성능 강화 (2세대 텐서 코어)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25.9302%;&quot;&gt;&lt;span data-path-to-node=&quot;17,2,4,0&quot;&gt;65 TFLOPS (FP16)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.1395%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-path-to-node=&quot;17,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,3,0,0&quot;&gt;Ampere&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.60465%;&quot;&gt;&lt;span data-path-to-node=&quot;17,3,1,0&quot;&gt;A100&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 10.2326%;&quot;&gt;&lt;span data-path-to-node=&quot;17,3,2,0&quot;&gt;108&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 36.8605%;&quot;&gt;&lt;span data-path-to-node=&quot;17,3,3,0&quot;&gt;TF32 도입, 주기당 연산량 2배 증가&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25.9302%;&quot;&gt;&lt;span data-path-to-node=&quot;17,3,4,0&quot;&gt;312 TFLOPS (FP16)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.1395%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-path-to-node=&quot;17,4,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,4,0,0&quot;&gt;Hopper&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.60465%;&quot;&gt;&lt;span data-path-to-node=&quot;17,4,1,0&quot;&gt;H100&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 10.2326%;&quot;&gt;&lt;span data-path-to-node=&quot;17,4,2,0&quot;&gt;132&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 36.8605%;&quot;&gt;&lt;span data-path-to-node=&quot;17,4,3,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,4,3,0&quot;&gt;Transformer Engine&lt;br /&gt;&lt;/b&gt;(FP8 &amp;lt;-&amp;gt; FP16 자동전환),&lt;br /&gt;FP8 도입&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25.9302%;&quot;&gt;&lt;span data-path-to-node=&quot;17,4,4,0&quot;&gt;1.98 PFLOPS (FP8)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.1395%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-path-to-node=&quot;17,5,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,5,0,0&quot;&gt;Blackwell&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.60465%;&quot;&gt;&lt;span data-path-to-node=&quot;17,5,1,0&quot;&gt;B200/&lt;br /&gt;GB200&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 10.2326%;&quot;&gt;&lt;span data-path-to-node=&quot;17,5,2,0&quot;&gt;~600&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 36.8605%;&quot;&gt;&lt;span data-path-to-node=&quot;17,5,3,0&quot;&gt;듀얼 다이 구조, FP4 지원&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25.9302%;&quot;&gt;&lt;span data-path-to-node=&quot;17,5,4,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,5,4,0&quot;&gt;20 PFLOPS (FP4)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.1395%;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,6,0,0&quot;&gt;Rubin&lt;/b&gt; (2026)&lt;span style=&quot;color: #000000;&quot; data-path-to-node=&quot;17,5,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,5,0,0&quot;&gt;&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.60465%;&quot;&gt;&lt;span data-path-to-node=&quot;17,5,1,0&quot;&gt;-&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 10.2326%;&quot;&gt;&lt;span data-path-to-node=&quot;17,5,2,0&quot;&gt;-&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 36.8605%;&quot;&gt;&lt;span data-path-to-node=&quot;17,5,3,0&quot;&gt;-&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25.9302%;&quot;&gt;&lt;span data-path-to-node=&quot;17,5,4,0&quot;&gt;-&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;19&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,0,0&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Ampere (A100)&lt;/span&gt;:&lt;/b&gt; SM당 주기별 연산 횟수를 256회로 높이고 처음으로 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;45&quot; data-path-to-node=&quot;19,0,0&quot;&gt;FP8&lt;/b&gt;&lt;/span&gt; 모드를 도입했습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,1,0&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Hopper (H100)&lt;/span&gt;:&lt;/b&gt; 주기당 연산 512회로 성능을 두 배 높여 처음으로 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;44&quot; data-path-to-node=&quot;19,1,0&quot;&gt;PFLOPS(Peta FLOPS)&lt;/b&gt;&lt;/span&gt; 시대에 진입했습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,2,0&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Blackwell (B200)&lt;/span&gt;:&lt;/b&gt; 듀얼 다이 구조와 160개의 SM(Ultra 기준)을 통해 H100 대비 추론 성능을 최대 30배까지 끌어올렸습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-path-to-node=&quot;18&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;19&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 핵심 기술 요소 상세 분석: SM과 HBM&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-path-to-node=&quot;20&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3.1. SM (Streaming Multiprocessor)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;21&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;21,0,0&quot;&gt;개념:&lt;/b&gt;&lt;/span&gt; GPU 내부에서 실제 연산을 수행하는 '핵심 계산 유닛'입니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;21,1,0&quot;&gt;진화:&lt;/b&gt;&lt;/span&gt; Ampere(A100)는 SM당 연산 처리 능력을 2배(128&amp;rarr;256 ops/cycle)로 높였고, Hopper(H100)는 다시 512회로 높였습니다. Blackwell은 SM 개수 자체를 600개까지 늘려 성능을 극대화했습니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;21,2,0&quot;&gt;공식:&lt;/b&gt;&lt;/span&gt; GPU 전체 성능 = [SM 개수 &amp;times; 주기당 연산 횟수 &amp;times; 동작 주파수]의 조합으로 결정됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-path-to-node=&quot;22&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3.2. HBM (High Bandwidth Memory)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;23&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;23,0,0&quot;&gt;개념:&lt;/b&gt;&lt;/span&gt; D램을 수직으로 쌓아 올려 데이터 전송 통로를 극대화한 고대역폭 메모리입니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;23,1,0&quot;&gt;필요성:&lt;/b&gt;&lt;/span&gt; AI 모델의 파라미터가 거대해짐에 따라, 연산 속도만큼 데이터를 읽어오는 속도가 중요해졌습니다. 이를 위해 GPU 바로 옆에 HBM을 배치하여 병목 현상을 해결합니다. (H100: HBM3 / Blackwell: HBM3e)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-path-to-node=&quot;24&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;25&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4. A100/H100 vs A800/H800: 규제가 만든 기술적 차이&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-path-to-node=&quot;26&quot; data-ke-size=&quot;size16&quot;&gt;'800' 시리즈는 미국 정부의 대중국 수출 제한 규정에 따라 성능을 인위적으로 조정한 모델입니다.&lt;/p&gt;
&lt;h4 data-path-to-node=&quot;27&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4.1. 수출 제한의 배경과 기준&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;28&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;28,0,0&quot;&gt;I/O 대역폭:&lt;/b&gt;&lt;/span&gt; 전송 속도 600GB/s 이상 규제&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;28,1,0&quot;&gt;컴퓨팅 파워:&lt;/b&gt;&lt;/span&gt; 특정 수치 이상의 연산 능력을 갖춘 칩의 수출을 금지함에 따라 성능을 조정한 하위 모델이 등장했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-path-to-node=&quot;29&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4.2. A800과 H800의 실제 사양 변화&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;30&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;30,0,0&quot;&gt;A800:&lt;/b&gt;&lt;/span&gt; 연산 성능은 A100과 동일하지만, &lt;b data-index-in-node=&quot;26&quot; data-path-to-node=&quot;30,0,0&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;NVLink 대역폭을 400GB/s로 하&lt;/span&gt;향&lt;/b&gt;했습니다. 이로 인해 다중 GPU 훈련 시 효율이 약 40% 감소합니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;30,1,0&quot;&gt;H800:&lt;/b&gt;&lt;/span&gt; 대역폭 조정은 물론, &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;18&quot; data-path-to-node=&quot;30,1,0&quot;&gt;FP64(배정밀도) 연산 성능을 1 TFLOPS 수준으로 제한&lt;/b&gt;&lt;/span&gt;했습니다. 이는 AI 학습 외에 고성능 과학 계산(HPC)이나 슈퍼컴퓨팅 용도로 활용되는 것을 차단하기 위한 조치입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-path-to-node=&quot;31&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;32&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;5. 전문성 향상을 위한 핵심 기술 용어 정리&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-path-to-node=&quot;33&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;5.1. 연산 성능 단위 (FLOPS)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;34&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;34,0,0&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;FLOPS&lt;/span&gt;:&lt;/b&gt; 초당 수행 가능한 부동소수점 연산 횟수입니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;34,1,0&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Tflops (Tera)&lt;/span&gt;:&lt;/b&gt; 1초에 1조 번 연산&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;34,2,0&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Pflops (Peta)&lt;/span&gt;:&lt;/b&gt; 1초에 1,000조 번 연산 (H100부터 본격 진입)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-path-to-node=&quot;35&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;5.2. 데이터 정밀도 (Precision)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;36&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;36,0,0&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;FP32&lt;/span&gt;:&lt;/b&gt; 고정밀 과학 계산용.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;36,1,0&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;FP16 / BF16&lt;/span&gt;:&lt;/b&gt; 현재 AI 학습의 표준 규격.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;36,2,0&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;FP8 / FP4&lt;/span&gt;:&lt;/b&gt; 최신 아키텍처에서 추론 속도를 극대화하기 위해 도입된 저정밀도 규격입니다. 비트 수가 낮을수록 연산은 빨라지지만 정밀도는 미세하게 조정됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.fibermall.com/ko/blog/nvidia-ai-chip.htm?srsltid=AfmBOooHXlRMceMJt6LPRgRXqmVmTgPhF490iqCD4eo-8bkUt14GSMhB&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.fibermall.com/ko/blog/nvidia-ai-chip.htm?srsltid=AfmBOooHXlRMceMJt6LPRgRXqmVmTgPhF490iqCD4eo-8bkUt14GSMhB&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://recording-it.tistory.com/122&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://recording-it.tistory.com/122&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS Fundamentals/CS</category>
      <category>A100</category>
      <category>blackwell</category>
      <category>GPU</category>
      <category>H100</category>
      <category>Nvidia</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/114</guid>
      <comments>https://striver.tistory.com/entry/CS-AI-%EC%8B%9C%EB%8C%80%EB%A5%BC-%EC%A7%80%EB%B0%B0%ED%95%98%EB%8A%94-%ED%95%98%EB%93%9C%EC%9B%A8%EC%96%B4-NVIDIA-GPU%EC%9D%98-%EB%AA%A8%EB%93%A0-%EA%B2%83#entry114comment</comments>
      <pubDate>Thu, 15 Jan 2026 17:10:10 +0900</pubDate>
    </item>
    <item>
      <title>[정규표현식] 데이터 전처리를 위한 정규표현식</title>
      <link>https://striver.tistory.com/entry/%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EC%B2%98%EB%A6%AC%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 파싱하고 정제하는 과정에서 정규표현식을 통해 원하는 내용의 값들만 추출하고, 변경하는 작업을 할 수 있습니다. 파이썬의 re 모듈을 활용한 정규표현식의 개념과 사용 예제에 대해 간략하게 살펴보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 정규표현식의 기초 : 패턴을 만드는 규칙&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규표현식은 특정 규칙을 가진 문자열의 집합을 표현하는 데 사용됩니다. 몇 가지 기본 특수 문자(패턴)만 알면 금방 익숙해질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;. (마침표)&lt;/b&gt;: 줄바꿈 문자(\n)를 제외한 모든 문자와 매치됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;\d&lt;/b&gt;: 숫자를 의미합니다. [0-9]와 같습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;\s&lt;/b&gt;: 공백 문자(스페이스, 탭, 줄바꿈 등)를 의미합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;\w&lt;/b&gt;: 알파벳, 숫자, 언더스코어(_)를 포함하는 문자입니다. [a-zA-Z0-9_]와 같습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;* (애스터리스크)&lt;/b&gt;: 바로 앞의 문자가 0번 이상 반복되는 경우와 매치됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;+ (플러스)&lt;/b&gt;: 바로 앞의 문자가 1번 이상 반복되는 경우와 매치됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;? (물음표)&lt;/b&gt;: 바로 앞의 문자가 0번 또는 1번 나타나는 경우와 매치됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[] (대괄호)&lt;/b&gt;: 괄호 안의 문자 중 하나와 매치됩니다. (예: [abc]는 'a', 'b', 'c' 중 하나)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;() (소괄호)&lt;/b&gt;: 그룹을 만들어 패턴을 묶거나, 매치된 부분을 캡처(추출)하는 데 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 외에도 다양한 특수 문자들이 있지만, 이 정도만 알아도 웬만한 패턴은 만들 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. re.compile()&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규 표현식 패턴을 미리 컴파일하여 re.Pattern 타입의 객체를 반환합니다. 같은 패턴을 여러 번 사용할 때 성능 상 유리하고, 미리 컴파일하여 재사용할 때 속도에서 이점을 얻을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[입력 예시]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752111180539&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import re

# 컴파일된 패턴
TITLE_PATTERN = re.compile(r'^([１２３４５６７８９０\d]+\.\s*)(.*)$')

# 입력 샘플
samples = [
    &quot;1. 파이썬 정규표현식&quot;,
    &quot;２. 두 번째 제목입니다.&quot;,
    &quot;10. 마지막 제목&quot;
]

for content in samples:
    title_match = TITLE_PATTERN.match(content)
    if title_match:
        # group(1)은 첫 번째 괄호에 매치된 부분, group(2)는 두 번째 괄호에 매치된 부분
        print(f&quot;입력: '{content}'&quot;)
        print(f&quot;  - 번호 부분: '{title_match.group(1)}'&quot;)
        print(f&quot;  - 제목 부분: '{title_match.group(2)}'\n&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;r
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;raw string을 의미합니다.&lt;/li&gt;
&lt;li&gt;Python 문자열에서 \는 이스케이프 문자입니다. \d, \s, \. 같은 정규 표현식 특수 문자를 제대로 전달하려면 \\d, \\s, \\.로 써야 하는데 raw string을 사용하면 백슬래시를 그대로 전달할 수 있어 가독성이 좋아지기때문에 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;^&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문자열의 시작을 의미합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;([１２３４５６７８９０\d]+\.\s*)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫 번째 캡처 그룹입니다.&lt;/li&gt;
&lt;li&gt;전각 숫자(1 , 2 ,...)와 반각 숫자(1,2,...)가 1번 이상 반복되고 마침표 문자가 있고 공백 문자가 0번 이상 반복되는 패턴을 찾습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;(.*)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;두 번째 캡처 그룹&lt;/span&gt;입니다.&lt;/li&gt;
&lt;li&gt;줄바꿈 문자를 제외한 모든 문자가 0번 이상 반복되는 패턴, 즉 나머지 모든 텍스트를 의미합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;$
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문자열의 끝을 의미합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;re.match()
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문자열의 처음부터 패턴이 일치하는지 확인합니다. 중간부터 일치하는 경우는 찾아내지 못합니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;매치가 성공하면 match 객체를 리턴하고 매치되지 않을때는 None을 리턴합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[실행 결과]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752111626607&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;입력: '1. 파이썬 정규표현식'
  - 번호 부분: '1. '
  - 제목 부분: '파이썬 정규표현식'

입력: '２. 두 번째 제목입니다.'
  - 번호 부분: '２. '
  - 제목 부분: '두 번째 제목입니다.'

입력: '10. 마지막 제목'
  - 번호 부분: '10. '
  - 제목 부분: '마지막 제목'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. re.split(pattern, string)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규표현식 패턴에 맞는 부분을 구분자로 하여 문자열을 분리한 뒤, 리스트로 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[입력 예시]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752111804594&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import re

# 입력 샘플 (데이터프레임의 한 열이라고 가정)
row = {'유형1': '건강/상해, 운전자, 종합보험'}

# re.split 사용
types = re.split(r'[/,]', row['유형1'])

# 각 요소를 순회하며 공백 제거 (x.strip() 활용)
cleaned_types = [x.strip() for x in types if x.strip()]

print(f&quot;입력: '{row['유형1']}'&quot;)
print(f&quot;분리 및 공백 제거 후: {cleaned_types}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;r'[/,]'
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;'/' 또는 ',' 문자를 의미하는 정규표현식입니다. [] 안에 넣으면 'or' 조건으로 작용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;x.strip()
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 요소를 순회하면 공백을 제거해서 쉼표 뒤의 공백까지 깔끔하게 제거하였습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[실행 결과]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752111947188&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;입력: '건강/상해, 운전자, 종합보험'
분리 및 공백 제거 후: ['건강', '상해', '운전자', '종합보험']&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4. re.findall(pattern, string)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패턴과 일치하는 모든 부분을 찾아 리스트로 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[입력 예시]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752112162656&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import re

# 입력 샘플
insurance_period = &quot;보험기간: 10년 (최대 100세 보장)&quot;

# 숫자만 모두 찾기
numbers = re.findall(r'\d+', insurance_period)
print(f&quot;입력: '{insurance_period}'&quot;)
print(f&quot;찾아낸 모든 숫자: {numbers}&quot;)

# 첫 번째 숫자만 선택하여 문자열로 변환
first_number_str = str(numbers[0])
print(f&quot;첫 번째 숫자(문자열): '{first_number_str}'&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;r'\d+'
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;숫자(\d)가 1번 이상 반복되는 패턴을 찾습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;numbers[0]
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;findall을 통해 반환된 리스트의 첫 번째 요소를 선택합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[실행 결과]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752112289225&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;입력: '보험기간: 10년 (최대 100세 보장)'
찾아낸 모든 숫자: ['10', '100']
첫 번째 숫자(문자열): '10'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;5. re.search(pattern, string)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열 전체를 검색하여 패턴과 일치하는 첫 번째 부분을 Match 객체로 반환하고, 없으면 None을 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[입력 예시]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752112383586&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import re

# 패턴
PATTERN = re.compile(r&quot;(건강심사|간편심사)(?:\(([^)]+)\))?형?&quot;)

# 입력 샘플
samples = [
    &quot;상품명: 무배당 건강심사형 보험&quot;,
    &quot;이것은 간편심사(325) 상품입니다.&quot;,
    &quot;그냥 간편심사 보험&quot;
]

for text in samples:
    match = PATTERN.search(text)
    if match:
        print(f&quot;입력: '{text}'&quot;)
        # group(1)은 첫 번째 괄호, group(2)는 두 번째 괄호(괄호 안 내용)
        print(f&quot;  - 심사 유형: '{match.group(1)}'&quot;)
        # group(2)는 괄호가 없는 경우 None이 됩니다.
        print(f&quot;  - 괄호 내용: '{match.group(2)}'\n&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(건강심사|간편심사)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫 번째 캡처 그룹입니다.&lt;/li&gt;
&lt;li&gt;&quot;건강심사&quot; 또는 &quot;간편심사&quot; 문자열과 일치합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;(?:\(([^)]+)\))?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(?: ... )
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;캡처하지 않는 그룹&lt;/b&gt;으로, 그룹으로 묶어주지만 match.group()으로 결과를 저장(캡처) 하지는 않습니다.&lt;/li&gt;
&lt;li&gt;패턴을 하나로 묶어주기만 할 뿐입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;\( , \)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;실제 괄호 '(' 와 ')' 문자를 찾습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;([^)]+)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;두 번째 캡처 그룹입니다.&lt;/li&gt;
&lt;li&gt;닫는 괄호를 제외한 모든 문자가 1번 이상 반복되는 패턴을 찾습니다. 즉, 괄호 안의 내용물을 의미합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;형?&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;형&quot; 이라는 글자가 있을 수도 있고 없을 수도 있다는 의미입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[실행 결과]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752113551922&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;입력: '상품명: 무배당 건강심사형 보험'
  - 심사 유형: '건강심사'
  - 괄호 내용: 'None'

입력: '이것은 간편심사(325) 상품입니다.'
  - 심사 유형: '간편심사'
  - 괄호 내용: '325'

입력: '그냥 간편심사 보험'
  - 심사 유형: '간편심사'
  - 괄호 내용: 'None'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://wikidocs.net/4308&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://wikidocs.net/4308&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>LLM Engineering/Data Preprocessing</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/113</guid>
      <comments>https://striver.tistory.com/entry/%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EC%B2%98%EB%A6%AC%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D#entry113comment</comments>
      <pubDate>Sun, 13 Jul 2025 18:17:32 +0900</pubDate>
    </item>
    <item>
      <title>[DataFrame] 전처리를 위한 DataFrame 함수들</title>
      <link>https://striver.tistory.com/entry/DataFrame</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 전처리과정에서 DataFrame을 많이 다루게 돼서 이참에 기본적이고 핵심적인 함수들을 한번 정리하고 가보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;예제 데이터 준비하기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1752025147719&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import pandas as pd
import numpy as np

# 예제용 샘플 데이터프레임
df = pd.DataFrame({
    '제품명': ['A-1', 'B-1', 'A-1', 'C-1', 'B-2', 'D-1'],
    '카테고리': ['가전', '가구', '가전', '가전', '가구', '주방'],
    '가격': [100, 150, 100, 120, 180, np.nan],
    '태그': [['신상', '인기'], '할인', ['신상', '인기'], '추천', '할인', np.nan]
}, index=['p1', 'p2', 'p3', 'p4', 'p5', 'p6'])

print(&quot;--- 원본 데이터 ---&quot;)
print(df)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[실행 결과]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752025170428&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;     제품명 카테고리     가격          태그
p1   A-1    가전  100.0  [신상, 인기]
p2   B-1    가구  150.0        할인
p3   A-1    가전  100.0  [신상, 인기]
p4   C-1    가전  120.0        추천
p5   B-2    가구  180.0        할인
p6   D-1    주방    NaN         NaN&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. columns : 열 이름 확인과 변경&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터프레임의 모든 열 이름을 확인하거나 변경할 때 사용합니다. 어떤 열들이 있는지 확인하고, 필요하다면 이름을 바꿀 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[사용 예시]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752025290471&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 현재 열 이름 확인하기
print(df.columns)

# 열 이름 전체를 영어로 변경하기
df_renamed = df.copy() # 원본 데이터는 소중하니까 복사해서 사용해요!
df_renamed.columns = ['Product', 'Category', 'Price', 'Tags']
print(&quot;\n--- 열 이름 변경 후 ---&quot;)
print(df_renamed.head(2))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[실행 결과]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752025309778&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Index(['제품명', '카테고리', '가격', '태그'], dtype='object')

--- 열 이름 변경 후 ---
    Product Category  Price         Tags
p1     A-1       가전  100.0  [신상, 인기]
p2     B-1       가구  150.0         할인&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. loc : 이름으로 데이터 선택하기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라벨(이름) 기반으로 행과 열을 선택할 수 있습니다. loc[행_라벨, 열_라벨] 형식으로 사용하고 사람이 보고 이해가기 가장 직관적인 방법이라고 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[사용 예시]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752025325601&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 1) 특정 행 선택하기 (p1 행의 모든 정보)
print(df.loc['p1'])

# 2) 여러 행과 특정 열 선택하기 (p1, p4 행의 제품명, 가격 정보)
print(&quot;\n&quot;, df.loc[['p1', 'p4'], ['제품명', '가격']])

# 3) 조건에 맞는 행 선택하기 (카테고리가 '가구'인 모든 데이터)
print(&quot;\n&quot;, df.loc[df['카테고리'] == '가구'])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[실행 결과]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752025325602&quot; class=&quot;bash&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;제품명            A-1
카테고리            가전
가격            100.0
태그       [신상, 인기]
Name: p1, dtype: object

      제품명     가격
p1   A-1  100.0
p4   C-1  120.0

     제품명 카테고리     가격   태그
p2   B-1    가구  150.0   할인
p5   B-2    가구  180.0   할인&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. iloc : 위치로 데이터 선택하기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0부터 시작하는 정수 위치(순서)로 데이터를 선택합니다. 코드로 특정 위치의 데이터를 가져올 때 매우 유용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[사용 예시]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752025326447&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 1) 첫 번째 행 선택하기 (0번 인덱스)
print(df.iloc[0])

# 2) 여러 행과 열 슬라이싱하기 (0~2번 행, 1~2번 열)
print(&quot;\n&quot;, df.iloc[0:3, 1:3])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[실행 결과]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752025326448&quot; class=&quot;bash&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;제품명            A-1
카테고리            가전
가격            100.0
태그       [신상, 인기]
Name: p1, dtype: object

     카테고리     가격
p1    가전  100.0
p2    가구  150.0
p3    가전  100.0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4. concat : 데이터프레임 합치기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러개로 나뉜 데이터를 하나로 합칠때 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[사용 예시]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752025327908&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 추가할 새로운 데이터
new_product = pd.DataFrame({'제품명': ['E-1'], '카테고리': ['생활'], '가격': [80], '태그': ['특가']})

# 기존 데이터프레임(df) 아래에 새로운 데이터(new_product)를 합치기
df_combined = pd.concat([df, new_product], ignore_index=True) # ignore_index=True 로 인덱스를 새로 부여
print(df_combined.tail(3))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[실행 결과]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752025327908&quot; class=&quot;bash&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;   제품명 카테고리     가격    태그
4  B-2    가구  180.0    할인
5  D-1    주방    NaN   NaN
6  E-1    생활   80.0    특가&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;5. drop_duplicates : 중복 데이터 제거하기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중복된 행을 제거합니다. subset으로 특정 열 기준 중복 검사가 가능하고, keep으로 어떤 중복값을 남길지 정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(p1, p3가 모든 데이터가 동일한 중복 행이라고 가정하겠습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[사용 예시]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752025328798&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 1) 기본 : 모든 열이 동일한 행 제거 (첫 번째 값 남김)
print(df.drop_duplicates())

# 2) keep='last' : 중복된 값 중 마지막 값 남기기
print(&quot;\n&quot;, df.drop_duplicates(keep='last'))

# 3) subset=['카테고리'] : '카테고리' 열 기준으로 중복 제거
print(&quot;\n&quot;, df.drop_duplicates(subset=['카테고리']))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[실행 결과]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752025328798&quot; class=&quot;bash&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;// (기본) p3 행이 제거됨
     제품명 카테고리     가격          태그
p1   A-1    가전  100.0  [신상, 인기]
p2   B-1    가구  150.0        할인
p4   C-1    가전  120.0        추천
p5   B-2    가구  180.0        할인
p6   D-1    주방    NaN         NaN

// (keep='last') p1 행이 제거됨
     제품명 카테고리     가격          태그
p2   B-1    가구  150.0        할인
p3   A-1    가전  100.0  [신상, 인기]
p4   C-1    가전  120.0        추천
p5   B-2    가구  180.0        할인
p6   D-1    주방    NaN         NaN

// (subset) 카테고리별로 처음 나타나는 행만 남김
     제품명 카테고리     가격          태그
p1   A-1    가전  100.0  [신상, 인기]
p2   B-1    가구  150.0        할인
p6   D-1    주방    NaN         NaN&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;6. reset_index : 인덱스 재설정하기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 정제 후 뒤죽박죽인 인덱스를 0부터 시작하는 깔끔한 정수 인덱스로 초기화합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[사용 예시]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752026094723&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1) 기본 : 기존 인덱스('p1', 'p2'...)가 'index'라는 새로운 열로 들어감
print(df.reset_index().head(2))

# 2) drop=True : 기존 인덱스를 열로 추가하지 않고 그냥 버림
print(&quot;\n&quot;, df.reset_index(drop=True).head(2))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[실행 결과]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752026127747&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  index 제품명 카테고리     가격          태그
0    p1   A-1    가전  100.0  [신상, 인기]
1    p2   B-1    가구  150.0        할인

   제품명 카테고리     가격          태그
0   A-1    가전  100.0  [신상, 인기]
1   B-1    가구  150.0        할인&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;7. insert : 원하는 위치에 열 삽입하기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원하는 위치에 열을 삽입합니다. insert(위치, 열_이름, 데이터) 형식으로 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[사용 예시]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752026161725&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1번 위치(두 번째)에 '판매량' 열 삽입하기
df_inserted = df.copy()
sales_data = [50, 30, 80, 120, 40, 10]
df_inserted.insert(1, '판매량', sales_data)
print(df_inserted.head(2))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[실행 결과]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752026177868&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;     제품명  판매량 카테고리     가격          태그
p1   A-1   50    가전  100.0  [신상, 인기]
p2   B-1   30    가구  150.0        할인&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;8. rename : 열 이름 부분 변경하기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;columns 속성은 전체 이름을 바꿀 때, rename 속성은 특정 열의 이름만 골라서 바꿀 때 편리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[사용 예시]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752026251588&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# '제품명' -&amp;gt; '상품명', '가격' -&amp;gt; '판매가'로 변경
df_renamed_part = df.rename(columns={'제품명': '상품명', '가격': '판매가'})
print(df_renamed_part.head(2))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[실행 결과]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752026265870&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;     상품명 카테고리     판매가          태그
p1   A-1    가전  100.0  [신상, 인기]
p2   B-1    가구  150.0        할인&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;9. explode : 리스트 데이터를 행으로 펼치기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 셀에 여러 값이 리스트로 담겨있을 때, 각 값을 개별 행으로 분리해주는 함수입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[사용 예시]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752026317471&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# '태그' 열의 리스트 값을 각각의 행으로 펼치기
df_exploded = df.explode('태그')
print(df_exploded)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[실행 결과]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752026331343&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;     제품명 카테고리     가격   태그
p1   A-1    가전  100.0   신상
p1   A-1    가전  100.0   인기
p2   B-1    가구  150.0   할인
p3   A-1    가전  100.0   신상
p3   A-1    가전  100.0   인기
p4   C-1    가전  120.0   추천
p5   B-2    가구  180.0   할인
p6   D-1    주방    NaN  NaN&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;10. isna/notna : 결측치 확인하기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터가 결측치인지 아닌지를 boolean 값으로 반환합니다. 보통 .sum()과 함께 사용하여 결측치 개수를 파악합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[사용 예시]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752026372705&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# isna() : 결측치면 True, 아니면 False
print(df.isna())

# isna().sum() : 열별 결측치 개수 파악
print(&quot;\n--- 열별 결측치 개수 ---&quot;)
print(df.isna().sum())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[실행 결과]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752026386198&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;       제품명   카테고리     가격     태그
p1  False   False  False  False
p2  False   False  False  False
p3  False   False  False  False
p4  False   False  False  False
p5  False   False  False  False
p6  False   False   True   True

--- 열별 결측치 개수 ---
제품명      0
카테고리     0
가격       1
태그       1
dtype: int64&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;11. fillna : 결측치 채우기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결측치를 특정 값으로 채워 데이터를 깔끔하게 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[사용 예시]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752026435048&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# '가격'의 결측치는 0으로, '태그'의 결측치는 '정보없음'으로 채우기
df_filled = df.copy()
df_filled['가격'] = df_filled['가격'].fillna(0)
df_filled['태그'] = df_filled['태그'].fillna('정보없음')
print(df_filled.tail(1)) # 마지막 행 확인&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[실행 결과]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752026448264&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;     제품명 카테고리    가격     태그
p6   D-1    주방   0.0  정보없음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;12. iterrows : 행 단위로 반복하기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;데이터프레임의 행을 하나씩 순회하며 &lt;b&gt;(인덱스, 행 데이터)&lt;/b&gt; 쌍을 반환합니다. for 문과 함께 사용하면 각 행의 데이터에 쉽게 접근할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;[사용 예시]&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752050066064&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 각 행을 순회하며 제품명과 카테고리를 출력
for index, row in df.iterrows():
    product_name = row['제품명']
    category = row['카테고리']
    print(f&quot;인덱스 '{index}' : {product_name} 제품은 {category} 카테고리에 속합니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[실행 결과]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752050085216&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;인덱스 'p1' : A-1 제품은 가전 카테고리에 속합니다.
인덱스 'p2' : B-1 제품은 가구 카테고리에 속합니다.
인덱스 'p3' : A-1 제품은 가전 카테고리에 속합니다.
인덱스 'p4' : C-1 제품은 가전 카테고리에 속합니다.
인덱스 'p5' : B-2 제품은 가구 카테고리에 속합니다.
인덱스 'p6' : D-1 제품은 주방 카테고리에 속합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;13. list와의 자유로운 변환&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pandas의 열(series)은 파이썬의 list와 쉽게 변환할 수 있어 데이터 활용도를 높여줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[사용 예시]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752026518040&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# '카테고리' 열을 리스트로 만들기
category_list = df['카테고리'].tolist()
print(category_list)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[실행 결과]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752026532817&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;['가전', '가구', '가전', '가전', '가구', '주방']&lt;/code&gt;&lt;/pre&gt;</description>
      <category>LLM Engineering/Data Preprocessing</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/112</guid>
      <comments>https://striver.tistory.com/entry/DataFrame#entry112comment</comments>
      <pubDate>Sat, 12 Jul 2025 12:06:40 +0900</pubDate>
    </item>
    <item>
      <title>[네트워크] 외부에서 사내 서버 접근</title>
      <link>https://striver.tistory.com/entry/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%99%B8%EB%B6%80%EC%97%90%EC%84%9C-%EC%82%AC%EB%82%B4-%EC%84%9C%EB%B2%84-%EC%A0%91%EA%B7%BC</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 서버에서 열려있는 포트 확인 방법&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1) netstate&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 일반적인 사용 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1751503239146&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 모든 열린 포트 확인
netstat -tuln

# 특정 포트 확인 (예: 8000번)
netstat -tuln | grep :8000

# 프로세스 정보까지 함께 보기
netstat -tulnp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2) ss&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최신 방법으로 netstat보다 빠른 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1751503267610&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 모든 열린 포트 확인
ss -tuln

# 특정 포트 확인
ss -tuln | grep :8000

# 프로세스 정보 포함
ss -tulnp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 결과 해석&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;534&quot; data-origin-height=&quot;46&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBUikM/btsO1UDNslq/weMZkuDQLgZWA2CkUHCKe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBUikM/btsO1UDNslq/weMZkuDQLgZWA2CkUHCKe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBUikM/btsO1UDNslq/weMZkuDQLgZWA2CkUHCKe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBUikM%2FbtsO1UDNslq%2FweMZkuDQLgZWA2CkUHCKe0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;534&quot; height=&quot;46&quot; data-origin-width=&quot;534&quot; data-origin-height=&quot;46&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1) 0.0.0.0:포트번호&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버가 가진 모든 IP 주소로 접근 가능&lt;/li&gt;
&lt;li&gt;서버 자체(localhost), 서버의 사내 IP, 인터넷에서(공인IP가 있고 방화벽이 허용한다면) 접근 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2) 127.0.0.1:포트번호&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오직 해당 서버 자체에서만 접근 가능&lt;/li&gt;
&lt;li&gt;서버에 직접 SSH로 접속해서 &quot;localhost:포트번호&quot; 또는 &quot;127.0.0.1:포트번호&quot;와 같은 형식으로 접근 가능&lt;/li&gt;
&lt;li&gt;접근 불가능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;같은 사내망의 다른 컴퓨터에서도 접근 불가&lt;/li&gt;
&lt;li&gt;외부 인터넷에서도 접근 불가&lt;/li&gt;
&lt;li&gt;서버의 실제 IP로도 접근 불가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3) LISTEN&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아직 연결되지 않았지만 &quot;열려 있는&quot; 상태&lt;/li&gt;
&lt;li&gt;포트를 지정한 도커 컨테이너를 실행하면 해당 포트가 LISTEN 상태로 출력되는걸 볼 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 포트 사용의 두 가지 방식&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포트 번호의 범위는 0~65535로 고정입니다. 그 이유는 TCP/UDP 헤더에서 포트 번호를 16비트(2바이트)로 정의하기 때문에 2^16=65536개 (0~655535)라서 물리적으로 한계가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1) 서버 사용&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개발자가 명시적으로 바인딩&lt;/li&gt;
&lt;li&gt;1~65535 중 아무 포트나 사용 가능 (1024 미만은 root 권한 필요)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2) 클라이언트 사용&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시스템이 32768~60999 중 자동 선택&lt;br /&gt;-&amp;gt; &quot;&lt;b&gt;cat /proc/sys/net/ipv4/ip_local_port_range&lt;/b&gt;&quot;으로 시스템에서 설정한 포트 자동 할당 범위 확인 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4. IP 확인&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1) 외부에서 보는 공인 IP&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751507611027&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl ifconfig.me&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 웹사이이트(ifconfig.me)에서 보는 나의 IP 주소로, 이 서버가 인터넷으로 나갈 때 사용하는 IP 주소를 의미합니다. 실행시 &quot;&lt;b&gt;203.123.45.67&lt;/b&gt;&quot;와 같이 공인 IP를 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2) 내 서버의 실제 IP&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751507753970&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ip route get 8.8.8.8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8.8.8.8(구글 DNS)에 패킷을 보낼 때 어떤 경로로 나가는가?&quot;를 확인함으로써 내 서버의 실제 IP를 의미합니다. 실행시 &quot;&lt;b&gt;8.8.8.8 via 192.168.1.1 dev eth0 src 203.123.45.67&lt;/b&gt;&quot;와 같은 결과를 확인할 수 있고, 여기서 src 뒤의 IP가 외부로 나갈 때 사용하는 내 서버의 IP&amp;nbsp; &amp;nbsp;주소를 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만일 두 IP가 같다면, 내 서버가 공인 IP를 직접 할당 받았다는 것을 의미하며, 외부에서 직접적으로 접근이 가능합니다. 다르다면, 인터넷과 내 서버 사이에 NAT/공유기가 존재한다는 사실을 알 수 있습니다. 이럴경우에 &quot;curl ifconfig.me&quot;로 출력되는 IP 주소는 공유기,회사 게이트웨이 등을 의미합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;5. 외부에서 사내 서버 접근하기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 외부에서 사내 서버에서 서빙중인 llm을 직접적으로 접근해서 api call을 하는것은 막혀있는 것으로 확인했습니다. 따라서 외부에서 사내 서버에 먼저 접속한 후 그 서버에서 필요한 소스코드를 다운받고 서버내에서 llm을 호출하는 방법을 사용하기로 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS Fundamentals/CS</category>
      <category>network</category>
      <category>네트워크</category>
      <category>사내 서버</category>
      <category>사내 서버 접근</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/111</guid>
      <comments>https://striver.tistory.com/entry/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%99%B8%EB%B6%80%EC%97%90%EC%84%9C-%EC%82%AC%EB%82%B4-%EC%84%9C%EB%B2%84-%EC%A0%91%EA%B7%BC#entry111comment</comments>
      <pubDate>Sat, 5 Jul 2025 14:09:54 +0900</pubDate>
    </item>
    <item>
      <title>claude code 체험</title>
      <link>https://striver.tistory.com/entry/claude-code-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;에이전트형 코딩 도구의 양대산맥인 커서와 윈드서프중 어떤걸 사용할지 고민하다가 더 최근에 떠오른 윈드서프를 써보자는 생각이 들어 찾아보던도중 클로드 코드라는 에이전트형 코딩 도구를 접하게 되었습니다. 어쩌다 뜨게 된 것일까 궁금해 찾아보니 윈드서프에서 플랫폼 운영을 위해 클로드 모델에 크게 의존하고 있었는데, 앤트로픽에서 일방적으로 모델 접근 제한을 통보하면서 자연스럽게 이슈가 된 것으로 파악했습니다. 이러한 결정은 경쟁사인 오픈AI의 윈드서프 인수도 한몫 하지 않았을까하는 개인적인 생각입니다. 마침 클로드 프로를 구독하고 있었기에 클로드 코드를 사용해보기로 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 npm을 사용하기 때문에 없으신분들은 별도로 설치후 진행해주시면 되겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1749599967813&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install -g @anthropic-ai/claude-code&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령어 실행 후 원하는 프로젝트 프로젝트 이동해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1749600650585&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd your-project-directory&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;claude code를 실행시켜줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1749600670210&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;claude&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 나오는 화면인데 모드를 설정하는 간단한 화면인 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;374&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJyW74/btsOwdQzqa0/KdC5DEpct9KKkz7kTREbQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJyW74/btsOwdQzqa0/KdC5DEpct9KKkz7kTREbQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJyW74/btsOwdQzqa0/KdC5DEpct9KKkz7kTREbQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJyW74%2FbtsOwdQzqa0%2FKdC5DEpct9KKkz7kTREbQk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;561&quot; height=&quot;374&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;374&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음실행시 앤트로픽 계정을 인증해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;984&quot; data-origin-height=&quot;452&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ee7eol/btsOu65vWmG/mzenLmL0EZowe6DjkMPKKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ee7eol/btsOu65vWmG/mzenLmL0EZowe6DjkMPKKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ee7eol/btsOu65vWmG/mzenLmL0EZowe6DjkMPKKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fee7eol%2FbtsOu65vWmG%2FmzenLmL0EZowe6DjkMPKKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;984&quot; height=&quot;452&quot; data-origin-width=&quot;984&quot; data-origin-height=&quot;452&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 이미 클로드 프로 계정을 보유하고 있었기에 1번을 선택하니 화면이 자연스럽게 이어졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1330&quot; data-origin-height=&quot;1230&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tVSmj/btsOu9OFY0T/LB3ornKcq1cdIhobEMALR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tVSmj/btsOu9OFY0T/LB3ornKcq1cdIhobEMALR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tVSmj/btsOu9OFY0T/LB3ornKcq1cdIhobEMALR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtVSmj%2FbtsOu9OFY0T%2FLB3ornKcq1cdIhobEMALR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1330&quot; height=&quot;1230&quot; data-origin-width=&quot;1330&quot; data-origin-height=&quot;1230&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;승인후 로그인이 완료된 모습입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;423&quot; data-origin-height=&quot;448&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhrrlh/btsOw5K7bGb/17fO08kkvKw9CrV71pWkk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhrrlh/btsOw5K7bGb/17fO08kkvKw9CrV71pWkk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhrrlh/btsOw5K7bGb/17fO08kkvKw9CrV71pWkk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbhrrlh%2FbtsOw5K7bGb%2F17fO08kkvKw9CrV71pWkk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;423&quot; height=&quot;448&quot; data-origin-width=&quot;423&quot; data-origin-height=&quot;448&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 몇몇 안내문구를 넘기고 클로드 코드 사용 화면을 접할 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;895&quot; data-origin-height=&quot;413&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eqvuU2/btsOutz9Ave/ZSGXqFUfXfcObK4lQynUd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eqvuU2/btsOutz9Ave/ZSGXqFUfXfcObK4lQynUd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eqvuU2/btsOutz9Ave/ZSGXqFUfXfcObK4lQynUd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeqvuU2%2FbtsOutz9Ave%2FZSGXqFUfXfcObK4lQynUd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;895&quot; height=&quot;413&quot; data-origin-width=&quot;895&quot; data-origin-height=&quot;413&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CLI 기반 AI 코딩 도구이다보니 터미널에서 진행했는데 자연어 명령을 통해 실행되며 &amp;lt;claude &quot;명령&quot;&amp;gt; 형식으로 실행시키는 것으로 확인했습니다. 앞으로 더 다양한 테스트를 통해 클로드 코드를 사용해보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://digitalbourgeois.tistory.com/813&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://digitalbourgeois.tistory.com/813&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/AI 활용</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/110</guid>
      <comments>https://striver.tistory.com/entry/claude-code-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0#entry110comment</comments>
      <pubDate>Sun, 15 Jun 2025 18:37:46 +0900</pubDate>
    </item>
    <item>
      <title>RAG Caching (/w Langchain &amp;amp; Langgraph)</title>
      <link>https://striver.tistory.com/entry/Langgraph-cache</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;LangGraph를 활용한 RAG 시스템의 챗봇을 과제로 진행하던 도중, 팀장님의 조언으로 동일한 질문에 대해 캐싱을 사용해 비용과 시간을 효율적으로 사용하라는 말씀을 들었습니다. 이에 따라 캐싱을 사용해 보다 더 효율적인 RAG 시스템을 구축하고자 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 문제상황&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 사용자의 같은 질문에 대해 소요된 시간과 토큰에 대한 이미지입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1778&quot; data-origin-height=&quot;854&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zobww/btsOuPPrlMI/fQmKPgNkLv4Q3Bx645Es4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zobww/btsOuPPrlMI/fQmKPgNkLv4Q3Bx645Es4K/img.png&quot; data-alt=&quot;첫번째 질문&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zobww/btsOuPPrlMI/fQmKPgNkLv4Q3Bx645Es4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fzobww%2FbtsOuPPrlMI%2FfQmKPgNkLv4Q3Bx645Es4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1778&quot; height=&quot;854&quot; data-origin-width=&quot;1778&quot; data-origin-height=&quot;854&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;첫번째 질문&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1818&quot; data-origin-height=&quot;992&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DBSgf/btsOuO325aw/CU1n7kijP5HL5V04UwjnR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DBSgf/btsOuO325aw/CU1n7kijP5HL5V04UwjnR0/img.png&quot; data-alt=&quot;두번째 질문&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DBSgf/btsOuO325aw/CU1n7kijP5HL5V04UwjnR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDBSgf%2FbtsOuO325aw%2FCU1n7kijP5HL5V04UwjnR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1818&quot; height=&quot;992&quot; data-origin-width=&quot;1818&quot; data-origin-height=&quot;992&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;두번째 질문&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보시다시피 같은 질문임에도 똑같은 노드 순환을 반복하며 시간과 토큰이 소모되는 모습입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. InMemoryCache (/w Langchain)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;langchain에서 캐싱을 위한 라이브러리를 제공해주어서 사용해보았습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1749541877242&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langchain.globals import set_llm_cache
from langchain_community.cache import InMemoryCache

class GraphBuilder:
    def __init__(self):
        set_llm_cache(InMemoryCache())
        self.graph = self._create_graph()
        self.tracer = OpikTracer(
            graph=self.graph.get_graph(xray=True), 
            project_name=&quot;agiledocs-rag&quot;
        )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와같은 코드를 사용해 graph를 생성하기전에, InMemoryCache를 생성해주었습니다. 적용전 이미지는 하단과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1800&quot; data-origin-height=&quot;842&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uKz9H/btsOuXUb7Yz/ST7S1L02TfsbRaIITBLpn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uKz9H/btsOuXUb7Yz/ST7S1L02TfsbRaIITBLpn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uKz9H/btsOuXUb7Yz/ST7S1L02TfsbRaIITBLpn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuKz9H%2FbtsOuXUb7Yz%2FST7S1L02TfsbRaIITBLpn0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1800&quot; height=&quot;842&quot; data-origin-width=&quot;1800&quot; data-origin-height=&quot;842&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;set_llm_cache(InMemoryCache()) 적용 후의 이미지입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1612&quot; data-origin-height=&quot;856&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0Cevo/btsOuyN8cD6/SgGbRCzkpZZvZ3UFDLKzC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0Cevo/btsOuyN8cD6/SgGbRCzkpZZvZ3UFDLKzC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0Cevo/btsOuyN8cD6/SgGbRCzkpZZvZ3UFDLKzC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0Cevo%2FbtsOuyN8cD6%2FSgGbRCzkpZZvZ3UFDLKzC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1612&quot; height=&quot;856&quot; data-origin-width=&quot;1612&quot; data-origin-height=&quot;856&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보시면 query_classifier node에서는 0초의 시간이 소모되지만, 다른 노드는 여전히 순환하는 모습을 볼 수 있습니다. 이는 Langchain에서 제공하는 set_llm_cache(InMemoryCache())가 LLM 호출만 캐싱을 수행하기 때문입니다. 그렇다면 검색과 리랭킹을 제외한 다른 부분에서는 캐싱이 이루어져야하지않나? 라는 생각이 들 수 있지만, 멀티턴 대화 구성을 위해 프롬프트에 이전 메시지들인 chat_history가 새롭게 누적되다보니 동적인 프롬프트가 되어서 캐싱이 이루어지지 않는 것 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. InMemoryCache (/w LangGraph)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 Langchain을 활용한 캐싱이 최선일까? 라는 생각에서 시작해서 LLM을 통해 생성한 답변은 캐싱하지 못하더라도 retrieve, rerank처럼 동일한 역할을 수행하는 정적인 노드에 대해서만 이라도 캐싱을 수행할 수는 없을까? 라는 생각이 들어서 찾아낸 것이 LangGraph에서 새롭게 고안해낸 노드 단위의 캐싱입니다. 아래는 샘플 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1749542791761&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langgraph.cache.memory import InMemoryCache
from langgraph.types import CachePolicy
import hashlib

class Cache:
    def query_classifier_cache_key(self,state : State) -&amp;gt; str:
        &quot;&quot;&quot;쿼리 분류기용 캐시 키 - 쿼리 내용만 사용&quot;&quot;&quot;
        query = state.get(&quot;query&quot;, &quot;&quot;)
        normalized_query = query.strip().lower()
        cache_key = hashlib.md5(normalized_query.encode()).hexdigest()
        return cache_key

workflow.add_node(&quot;query_classifier&quot;, queryclassifier.classification, cache_policy=CachePolicy(key_func=cache.query_classifier_cache_key))
workflow.compile(cache=InMemoryCache())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐싱을 하고자하는 노드에 CachePolicy()를 사용해주었고, 인자로 ttl과 key_func를 사용할 수 있습니다. ttl은 &lt;b&gt;캐싱 유효시간&lt;/b&gt;을 의미하고 key_func는 &lt;b&gt;캐싱의 키&lt;/b&gt;값을 반환하는 함수를 호출합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구체적인 사용 흐름은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;노드 실행전 LangGraph가 key_func를 호출해서 캐시 키 생성&lt;/li&gt;
&lt;li&gt;생성된 키로 이전 결과 검색&lt;/li&gt;
&lt;li&gt;값이 있으면 노드 실행 skip 후 바로 결과 반환, 없으면 노드 실행후 결과를 캐시에 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 쿼리에 대해서는 항상 동일한 캐시 키값을 생성하도록 코드를 작성했기 때문에, 이전에 같은 질문을 했다면 캐싱된 값을 반환하게됩니다. 적용전 이미지는 하단과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1826&quot; data-origin-height=&quot;1038&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yZbGE/btsOsWCJ6UE/KYd3pnrn88fcUzRoke40Yk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yZbGE/btsOsWCJ6UE/KYd3pnrn88fcUzRoke40Yk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yZbGE/btsOsWCJ6UE/KYd3pnrn88fcUzRoke40Yk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyZbGE%2FbtsOsWCJ6UE%2FKYd3pnrn88fcUzRoke40Yk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1826&quot; height=&quot;1038&quot; data-origin-width=&quot;1826&quot; data-origin-height=&quot;1038&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적용 후의 이미지입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1834&quot; data-origin-height=&quot;1174&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2IpNt/btsOu6cmuYD/CRxDwlAkOi95bCevCiJiSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2IpNt/btsOu6cmuYD/CRxDwlAkOi95bCevCiJiSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2IpNt/btsOu6cmuYD/CRxDwlAkOi95bCevCiJiSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2IpNt%2FbtsOu6cmuYD%2FCRxDwlAkOi95bCevCiJiSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1834&quot; height=&quot;1174&quot; data-origin-width=&quot;1834&quot; data-origin-height=&quot;1174&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간도 토큰도 확실히 절약된 모습을 볼 수 있었습니다. 여기서 Langchain의 캐싱과 다른 모습을 발견할 수 있는데, LangGraph의&amp;nbsp; 캐싱은 일치하는 키값이 있으면 바로 값을 반환하기때문에 노드를 아에 순환도 하지 않습니다. 반면, Langchain의 캐싱은 0초이긴하지만 노드 자체는 순환한다는 점에서 차별점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 노드를 아에 순환도 하지않는데 캐싱으로 반환하는 값이 무엇이길래 응답을 잘 하는 것이지?하고 생각이 들어서 이 부분도 확인해보았습니다. 처음에는 Multi-turn으로 구성하다보니 이전 대화에서 사용한 state가 다음 대화 기록에 사용 돼서 응답이 정상적으로 이루어지는건 아닐까? 하고 의심했었습니다. 그래서 질문 A,B가 있다고할때 A-&amp;gt;B-&amp;gt;A 순서로 질문을 해서 B의 state를 담은 상태에서 다시 A질문을 해보았습니다. 그런데도 정상 응답이 나오는걸 확인하고, 이전에 어떤 질문을 하던 상관없이 동일한 질문에 해당하는 값을 캐싱으로 반환할때 이전에 생성했던 상태값을 그대로 반환해준다는 것을 알게되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 아래는 Langchain, Langgraph의 캐싱을 모두 적용시킨 후 캐싱이 됐을 때 모습입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1890&quot; data-origin-height=&quot;1002&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Jaf1t/btsOuy9v8yn/Kz4aCMwkKYTesIXKEnbpXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Jaf1t/btsOuy9v8yn/Kz4aCMwkKYTesIXKEnbpXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Jaf1t/btsOuy9v8yn/Kz4aCMwkKYTesIXKEnbpXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJaf1t%2FbtsOuy9v8yn%2FKz4aCMwkKYTesIXKEnbpXk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1890&quot; height=&quot;1002&quot; data-origin-width=&quot;1890&quot; data-origin-height=&quot;1002&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;query_classifier node가 나오지 않는 모습을 보아, LangGraph의 캐싱이 Langchain의 캐싱보다 우선수위가 높다는 것을 알 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a href=&quot;https://python.langchain.com/docs/how_to/llm_caching/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://python.langchain.com/docs/how_to/llm_caching/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://langchain-ai.github.io/langgraph/concepts/low_level/?_gl=1*puicu9*_gcl_au*MTAwNTc3NjE5MS4xNzQ5NTEzOTg2*_ga*NDg4NDUxMjExLjE3NDk0NTI1NDc.*_ga_47WX3HKKY2*czE3NDk1MTM5ODUkbzUkZzAkdDE3NDk1MTM5ODckajU4JGwwJGgw#node-caching&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://langchain-ai.github.io/langgraph/concepts/low_level/?_gl=1*puicu9*_gcl_au*MTAwNTc3NjE5MS4xNzQ5NTEzOTg2*_ga*NDg4NDUxMjExLjE3NDk0NTI1NDc.*_ga_47WX3HKKY2*czE3NDk1MTM5ODUkbzUkZzAkdDE3NDk1MTM5ODckajU4JGwwJGgw#node-caching&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>LLM Engineering/RAG</category>
      <category>cache</category>
      <category>cachepolicy</category>
      <category>inmemorycache</category>
      <category>LangChain</category>
      <category>langchain caching</category>
      <category>langgraph</category>
      <category>langgraph caching</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/109</guid>
      <comments>https://striver.tistory.com/entry/Langgraph-cache#entry109comment</comments>
      <pubDate>Sat, 14 Jun 2025 18:24:41 +0900</pubDate>
    </item>
    <item>
      <title>[LangGraph] LangGraph 기초 복습</title>
      <link>https://striver.tistory.com/entry/LangGraph-LangGraph-%EA%B8%B0%EC%B4%88-%EB%B3%B5%EC%8A%B5</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;LangGraph의 기본적인 개념에 대해 복습하는 시간을 가져보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. LangGraph란?&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangGraph는 LLM 기반 워크플로우에 순환 연산 기능을 추가하여 복잡한 AI 애플리케이션의 흐름을 효과적으로 제어할 수 있는 프레임워크입니다. 노드(Node), 엣지(Edge), 상태(State)라는 세 가지 핵심 요소를 통해 RAG 파이프라인과 같은 복잡한 시스템을 유연하게 구성할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Node, Edge, State 를 통해 LLM을 활용한 워크플로우에 순환 연산 기능을 추가하여 손쉽게 흐름을 제어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- RAG 파이프라인의 세부 단계별 흐름 제어가 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Conditional Edge를 통한 조건부 흐름 제어 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Human-in-th-loop를 통해 필요시 중간에 개입하여 다음 단계 결정이 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Checkpointer 기능으로 과거 실행 과정에 대한 &quot;수정&quot; 및 &quot;리플레이&quot; 기능 제공&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 핵심 구성 요소&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2.1 State (상태)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태는 노드 간에 정보를 전달하는 데이터 컨테이너입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1174&quot; data-origin-height=&quot;466&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dHLCGW/btsNKt8HiMC/7HQ98OpxEujwv6682O2yJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dHLCGW/btsNKt8HiMC/7HQ98OpxEujwv6682O2yJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dHLCGW/btsNKt8HiMC/7HQ98OpxEujwv6682O2yJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdHLCGW%2FbtsNKt8HiMC%2F7HQ98OpxEujwv6682O2yJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1174&quot; height=&quot;466&quot; data-origin-width=&quot;1174&quot; data-origin-height=&quot;466&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TypedDict: 일반 파이썬 딕셔너리에 타입힌팅을 추가한 구조&lt;/li&gt;
&lt;li&gt;부분 업데이트: 모든 값을 채울 필요 없이 필요한 필드만 업데이트 가능&lt;/li&gt;
&lt;li&gt;Overwrite 방식: 새로운 노드에서 동일한 키의 값을 덮어쓰는 방식으로 상태 갱신&lt;/li&gt;
&lt;li&gt;Reducer: add_messages나 operator.add와 같은 함수로 자동으로 리스트에 항목 추가&lt;/li&gt;
&lt;li&gt;Annotated: 타입 외에 키값에 대한 추가 정보 제공 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2.2 Node (노드)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드는 워크플로우의 개별 단계를 정의하는 함수입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1167&quot; data-origin-height=&quot;364&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbv86F/btsNKuzIug1/yoIkONofQfUMUTjIgk1KT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbv86F/btsNKuzIug1/yoIkONofQfUMUTjIgk1KT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbv86F/btsNKuzIug1/yoIkONofQfUMUTjIgk1KT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbbv86F%2FbtsNKuzIug1%2FyoIkONofQfUMUTjIgk1KT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1167&quot; height=&quot;364&quot; data-origin-width=&quot;1167&quot; data-origin-height=&quot;364&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수로 정의&lt;/li&gt;
&lt;li&gt;입력인자 : State&lt;/li&gt;
&lt;li&gt;반환 : 대부분 State (Conditional Edge의 경우 다를 수 있음)&lt;/li&gt;
&lt;li&gt;add_node(&quot;노드이름&quot;,함수)로 노드 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2.3 Edge (엣지)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엣지는 노드 간의 연결을 정의합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1241&quot; data-origin-height=&quot;153&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eHx9ZM/btsNLpkp8sW/utXgEYY7MDErLCDCWn2sf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eHx9ZM/btsNLpkp8sW/utXgEYY7MDErLCDCWn2sf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eHx9ZM/btsNLpkp8sW/utXgEYY7MDErLCDCWn2sf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeHx9ZM%2FbtsNLpkp8sW%2FutXgEYY7MDErLCDCWn2sf0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1241&quot; height=&quot;153&quot; data-origin-width=&quot;1241&quot; data-origin-height=&quot;153&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;노드에서 노드간의 연결&lt;/li&gt;
&lt;li&gt;add_edge(&quot;노드이름&quot;,&quot;노드이름&quot;)로 노드 연결&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1185&quot; data-origin-height=&quot;374&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oMprO/btsNL3OGZSe/Tqn2TqPkeJ32yAoV2SzkNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oMprO/btsNL3OGZSe/Tqn2TqPkeJ32yAoV2SzkNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oMprO/btsNL3OGZSe/Tqn2TqPkeJ32yAoV2SzkNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoMprO%2FbtsNL3OGZSe%2FTqn2TqPkeJ32yAoV2SzkNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1185&quot; height=&quot;374&quot; data-origin-width=&quot;1185&quot; data-origin-height=&quot;374&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;노드에 조건부 엣지를 추가해 분기 수행 가능&lt;/li&gt;
&lt;li&gt;add_conditional_edges(&quot;노드이름&quot;, 조건부 판단 함수, dict 로 다음 단계 결정)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 그래프 구성 및 실행&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3.1 시작점 지정&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;746&quot; data-origin-height=&quot;103&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dSt2jf/btsNLr3lUUL/9OI1I2MkWTHitHuvozlbVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dSt2jf/btsNLr3lUUL/9OI1I2MkWTHitHuvozlbVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dSt2jf/btsNLr3lUUL/9OI1I2MkWTHitHuvozlbVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdSt2jf%2FbtsNLr3lUUL%2F9OI1I2MkWTHitHuvozlbVk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;746&quot; height=&quot;103&quot; data-origin-width=&quot;746&quot; data-origin-height=&quot;103&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;set_entry_point(&quot;노드이름&quot;)&lt;/li&gt;
&lt;li&gt;지정한 시작점부터 Graph가 시작&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3.2 체크포인터(Checkpointer)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;체크포인터는 그래프의 실행 흐름과 상태를 추적하고 저장합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;257&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VZ6aD/btsNKxDfE9t/tp60c9zM7d2k53Uk5m9dg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VZ6aD/btsNKxDfE9t/tp60c9zM7d2k53Uk5m9dg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VZ6aD/btsNKxDfE9t/tp60c9zM7d2k53Uk5m9dg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVZ6aD%2FbtsNKxDfE9t%2Ftp60c9zM7d2k53Uk5m9dg0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;980&quot; height=&quot;257&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;257&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Checkpointer : 각 노드간 실행결과를 추적하기 위한 메모리(대화에 대한 기록과 유사 개념)&lt;/li&gt;
&lt;li&gt;체크 포인터를 활용하여 특정 시점(Snapshot)으로 되돌리기 기능도 가능&lt;/li&gt;
&lt;li&gt;compile(checkpointer=memory)로 지정하여 그래프 생성 &lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3.3 그래프 실행&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1142&quot; data-origin-height=&quot;435&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNPQWH/btsNJT021bh/pxsU9lUz4S0lwLFi4lRW5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNPQWH/btsNJT021bh/pxsU9lUz4S0lwLFi4lRW5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNPQWH/btsNJT021bh/pxsU9lUz4S0lwLFi4lRW5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNPQWH%2FbtsNJT021bh%2FpxsU9lUz4S0lwLFi4lRW5K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1142&quot; height=&quot;435&quot; data-origin-width=&quot;1142&quot; data-origin-height=&quot;435&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;RunnableConfig&lt;br /&gt;recursion_limit : 최대 노드 실행 개수를 지정&lt;br /&gt;thread_id : 그래프 실행 아이디를 기록하고, 추후 추적하기 위한 목적으로 활용&lt;/li&gt;
&lt;li&gt;invoke(상태,config)로 전달하여 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;참고자료&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=W_uwR_yx4-c&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=W_uwR_yx4-c&lt;/a&gt;&lt;/p&gt;</description>
      <category>LLM Engineering/LLMops</category>
      <category>edge</category>
      <category>langgraph</category>
      <category>node</category>
      <category>state</category>
      <category>테디노트</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/107</guid>
      <comments>https://striver.tistory.com/entry/LangGraph-LangGraph-%EA%B8%B0%EC%B4%88-%EB%B3%B5%EC%8A%B5#entry107comment</comments>
      <pubDate>Fri, 2 May 2025 22:20:31 +0900</pubDate>
    </item>
    <item>
      <title>[Prompt Engineering] 프롬프트 엔지니어링의 심화 기법들(2)</title>
      <link>https://striver.tistory.com/entry/Prompt-Engineering-%ED%94%84%EB%A1%AC%ED%94%84%ED%8A%B8-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4%EB%A7%81%EC%9D%98-%EC%8B%AC%ED%99%94-%EA%B8%B0%EB%B2%95%EB%93%A42</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;언어 모델의 성능이 지속적으로 발전함에 따라, 이를 더 효과적으로 활용하기 위한 프롬프트 엔지니어링 기법도 진화하고 있습니다. 기본적인 프롬프트 기법을 넘어, 최근에는 AI의 추론 능력을 극대화하고 더 정확한 답변을 얻기 위한 심화 기법들이 연구되고 있습니다. 이 글에서는 최신 프롬프트 엔지니어링 심화 기법들을 살펴보고, 이를 활용하는 방법에 대해 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. Automatic Prompt Engineer (APE): 자동화된 프롬프트 최적화&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 개념&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자동 프롬프트 엔지니어(APE)는 최적의 프롬프트를 자동으로 생성하는 프레임워크&lt;/li&gt;
&lt;li&gt;인간의 개입 없이 여러 단계의 평가 과정을 통해 최적의 명령어를 생성하고 선택&lt;/li&gt;
&lt;li&gt;언어 모델 자체를 활용하여 더 나은 프롬프트를 발견하는 메타-프롬프팅 접근법&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 작동 과정: 6단계 프로세스&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;1단계: 후보 명령어 제안&lt;/b&gt; - 다양한 잠재적 프롬프트 생성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;2단계: 점수 매기기&lt;/b&gt; - 각 후보 프롬프트의 효과성 평가&lt;/li&gt;
&lt;li&gt;&lt;b&gt;3단계: 낮은 점수 후보 제거&lt;/b&gt; - 성능이 떨어지는 프롬프트 필터링&lt;/li&gt;
&lt;li&gt;&lt;b&gt;4단계: 높은 점수 후보 선정&lt;/b&gt; - 상위 성능 프롬프트 식별&lt;/li&gt;
&lt;li&gt;&lt;b&gt;5단계: 샘플링 모델 사용&lt;/b&gt; (선택사항) - 다양성 확보를 위한 샘플링&lt;/li&gt;
&lt;li&gt;&lt;b&gt;6단계: 최종 후보 선정&lt;/b&gt; - 최적의 프롬프트 결정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 주요 성과&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;APE가 발견한 프롬프트 예시: &quot;Let's work this out in a step by step way to be sure we have the right answer.&quot;&lt;/li&gt;
&lt;li&gt;이러한 자동 생성 프롬프트가 인간이 설계한 프롬프트보다 특정 작업에서 더 나은 성능을 보이는 경우도 있음&lt;/li&gt;
&lt;li&gt;특히 복잡한 추론 작업에서 효과적인 프롬프트 패턴 발견 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) 활용 가치&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프롬프트 엔지니어링의 시간과 노력 절감&lt;/li&gt;
&lt;li&gt;인간이 생각하지 못한 효과적인 프롬프트 패턴 발견&lt;/li&gt;
&lt;li&gt;작업별 최적화된 프롬프트 자동 생성 가능성&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. Active-Prompt: 동적 프롬프트 최적화&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 개념&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LLM이 능동적으로 프롬프트를 생성하고 수정하는 방법&lt;/li&gt;
&lt;li&gt;Chain-of-Thought(CoT) 추론의 한계를 극복하기 위한 접근법&lt;/li&gt;
&lt;li&gt;다양한 작업에 특화된 예시 프롬프트를 자동으로 개선&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) CoT 기법의 한계 극복&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인간이 만든 예시에 의존하는 기존 CoT의 한계 해소&lt;/li&gt;
&lt;li&gt;어렵고 모호한 질문에 대해서도 효과적인 예시 자동 생성&lt;/li&gt;
&lt;li&gt;사람이 질문-응답 쌍을 수작업으로 만드는 한계 극복&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 작동 원리&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초기 프롬프트에서 시작해 모델이 스스로 더 나은 예시 생성&lt;/li&gt;
&lt;li&gt;생성된 예시를 평가하고 가장 효과적인 예시를 선택&lt;/li&gt;
&lt;li&gt;선택된 예시를 활용해 최종 프롬프트 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) 효과적인 활용 영역&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;계속 변화하는 복잡한 도메인의 질문 처리&lt;/li&gt;
&lt;li&gt;다양한 유형의 추론 작업에 적응적 프롬프트 생성&lt;/li&gt;
&lt;li&gt;도메인 전문성이 필요한 특수 분야 질의응답&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. Directional Stimulus Prompting: 방향성을 가진 자극 프롬프팅&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 개념&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델의 출력을 원하는 방향으로 유도하는 프레임워크&lt;/li&gt;
&lt;li&gt;방향성을 가진 자극을 통해 AI 모델이 특정 목표를 향해 나아가도록 함&lt;/li&gt;
&lt;li&gt;힌트를 생성하는 별도의 작은 모델(T5 등)을 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 작동 방식&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력값에 대한 보조 프롬프트(힌트) 생성&lt;/li&gt;
&lt;li&gt;생성된 요약에 특정 키워드나 개념을 포함하도록 유도&lt;/li&gt;
&lt;li&gt;원하는 출력 방향으로 모델을 안내하는 자극 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 적용 사례&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요약 작업에서 특정 주제나 관점 강조&lt;/li&gt;
&lt;li&gt;대화 응답 생성 시 특정 톤이나 스타일 유도&lt;/li&gt;
&lt;li&gt;사고 추론 과정에서 특정 방법론이나 프레임워크 활용 유도&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) 주요 장점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델 출력의 예측 가능성 향상&lt;/li&gt;
&lt;li&gt;사용자 의도에 더 부합하는 결과물 생성&lt;/li&gt;
&lt;li&gt;특정 영역에 집중된 고품질 출력 생성 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4. ReAct: 추론과 행동의 통합적 접근&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 개념&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Reasoning(추론)과 Action(행동)을 교차적으로 사용하는 프롬프팅 방식&lt;/li&gt;
&lt;li&gt;모델이 생각하고, 행동하고, 결과를 관찰하는 순환적 프로세스&lt;/li&gt;
&lt;li&gt;외부 지식 소스와의 상호작용을 통합한 문제 해결 프레임워크&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 핵심 구성 요소&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Reasoning(추론)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델의 행동 계획 수립&lt;/li&gt;
&lt;li&gt;관찰 결과 추적 및 분석&lt;/li&gt;
&lt;li&gt;다음 단계 전략 수립 및 예외 상황 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Action(행동)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외부 지식 베이스나 환경과 상호작용&lt;/li&gt;
&lt;li&gt;정보 검색 및 도구 활용&lt;/li&gt;
&lt;li&gt;실제 행동 실행 및 결과 관찰&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 주요 장점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;문제 해결 능력 향상&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;순차적 정보 검색 및 활용 가능&lt;/li&gt;
&lt;li&gt;복잡한 다단계 문제에 체계적 접근&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;적응력 증가&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이전 단계의 결과에 기반한 동적 전략 수정&lt;/li&gt;
&lt;li&gt;예상치 못한 상황에 유연하게 대응&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;정보 신뢰성 향상&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외부 검증 가능한 소스 활용&lt;/li&gt;
&lt;li&gt;추론과 검색의 균형을 통한 정확도 증가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) 활용 시나리오&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;복잡한 질문에 대한 사실 기반 답변 생성&lt;/li&gt;
&lt;li&gt;외부 도구와 통합된 문제 해결(계산기, 검색 엔진 등)&lt;/li&gt;
&lt;li&gt;단계적 의사결정이 필요한 복잡한 작업 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;5. AI 모델의 추론 능력을 끌어올리는 핵심 전략&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 단계적 사고 과정 유도&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;복잡한 문제를 작은 단계로 분해하도록 안내&lt;/li&gt;
&lt;li&gt;&quot;Let's break this down step by step&quot;과 같은 지시어 활용&lt;/li&gt;
&lt;li&gt;중간 과정과 결론을 명확히 구분하도록 구조화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 생각할 시간/여유 제공&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델이 충분히 추론할 수 있는 공간 확보&lt;/li&gt;
&lt;li&gt;&quot;Take your time to think about this thoroughly&quot;와 같은 유도 표현 사용&lt;/li&gt;
&lt;li&gt;초기 직관적 답변 이후 재고려 및 검증 단계 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 다양한 시각에서 사고하도록 유도&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 관점에서 문제를 바라보도록 요청&lt;/li&gt;
&lt;li&gt;&quot;Consider this from multiple perspectives&quot;와 같은 프롬프트 활용&lt;/li&gt;
&lt;li&gt;대안적 해결책과 접근법을 탐색하도록 장려&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;패스트 캠퍼스 - 국내 공채 1호 프롬프트 엔지니어 강수진의 프롬프트 엔지니어링 A to Z&lt;/span&gt;&lt;/p&gt;</description>
      <category>LLM Engineering/LLMops</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/106</guid>
      <comments>https://striver.tistory.com/entry/Prompt-Engineering-%ED%94%84%EB%A1%AC%ED%94%84%ED%8A%B8-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4%EB%A7%81%EC%9D%98-%EC%8B%AC%ED%99%94-%EA%B8%B0%EB%B2%95%EB%93%A42#entry106comment</comments>
      <pubDate>Thu, 1 May 2025 22:32:35 +0900</pubDate>
    </item>
    <item>
      <title>[Prompt Engineering] 프롬프트 엔지니어링의 심화 기법들(1)</title>
      <link>https://striver.tistory.com/entry/Prompt-Engineering-%ED%94%84%EB%A1%AC%ED%94%84%ED%8A%B8-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4%EB%A7%81%EC%9D%98-%EC%8B%AC%ED%99%94-%EA%B8%B0%EB%B2%95%EB%93%A41</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;대규모 언어 모델(LLM)의 능력이 발전함에 따라, 더 정교하고 효과적인 프롬프트 엔지니어링 기법들이 등장하고 있습니다. 기본적인 Zero-shot, Few-shot, Chain-of-Thought 프롬프팅을 넘어서, 복잡한 문제 해결과 정확도 향상을 위한 고급 프롬프트 기법들을 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. Generate Knowledge Prompting (GoT): 지식 기반 추론 강화&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 개념&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;언어 모델이 추론 전에 관련 지식을 먼저 생성하도록 하는 방법&lt;/li&gt;
&lt;li&gt;답변 생성 전 모델이 스스로 관련 정보를 가져오고 활용하는 방식&lt;/li&gt;
&lt;li&gt;할루시네이션(환각) 현상을 완화하기 위한 효과적인 접근법&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 주요 목적&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델의 환각 현상 감소 및 사실 기반 답변 강화&lt;/li&gt;
&lt;li&gt;추가 지식을 통한 추론 능력 향상&lt;/li&gt;
&lt;li&gt;오픈 Q-A 테스크 방식을 활용한 자발적 지식 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 작동 방식: 3단계 프로세스&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;1단계: 지식 생성&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Few-shot 프롬프팅을 사용해 모델로부터 관련 정보와 사실 생성&lt;/li&gt;
&lt;li&gt;질문과 관련된 배경 지식, 개념, 원리 등을 명시적으로 끌어냄&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;2단계: 지식 통합&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생성된 지식을 프롬프트에 통합&lt;/li&gt;
&lt;li&gt;지식의 신뢰도 평가 및 관련성 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;3단계: 답변 생성&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;확보한 지식을 바탕으로 최종 답변 도출&lt;/li&gt;
&lt;li&gt;근거 기반의 정확한 추론 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) 프롬프트 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;질문: 식물의 광합성 과정에서 빛 에너지는 어떻게 화학 에너지로 변환되나요?

단계 1: 이 질문에 답하기 위해 필요한 관련 지식을 먼저 정리해보세요.

[지식 생성 공간]
광합성은 식물이 빛 에너지를 이용해 이산화탄소와 물로부터 포도당을 합성하는 과정입니다.
광합성의 주요 단계는 명반응과 암반응으로 나눌 수 있습니다.
명반응은 엽록체의 틸라코이드 막에서 일어나며, 빛 에너지를 흡수해 ATP와 NADPH 형태의 화학 에너지로 변환합니다.
빛 에너지는 광계 I과 광계 II의 엽록소 분자에 의해 포착됩니다.
광계 II에서는 물 분자를 분해해 전자를 방출하고, 이 전자는 전자 전달계를 통해 ATP를 생성합니다.
광계 I에서는 추가 빛 에너지를 흡수하여 NADP+를 NADPH로 환원시킵니다.
암반응(캘빈 회로)에서는 명반응에서 생성된 ATP와 NADPH를 사용하여 이산화탄소를 포도당으로 고정합니다.

단계 2: 위 정보를 바탕으로 질문에 답변해주세요.

[답변 생성 공간]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5) 장점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사실에 기반한 정확도 향상&lt;/li&gt;
&lt;li&gt;복잡한 도메인 지식이 필요한 질문에 효과적&lt;/li&gt;
&lt;li&gt;모델의 추론 과정이 투명하게 드러남&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6) 한계점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지식 생성 단계에서의 오류가 최종 답변에 영향&lt;/li&gt;
&lt;li&gt;프롬프트 길이 증가로 인한 토큰 소비 증가&lt;/li&gt;
&lt;li&gt;간단한 질문에는 불필요한 오버헤드 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. Prompt Chaining: 복잡한 작업의 단계적 분해&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 개념&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;복잡한 작업을 작고 관리하기 쉬운 하위 작업으로 나누는 기법&lt;/li&gt;
&lt;li&gt;각 하위 작업을 별도의 프롬프트로 처리하는 연쇄적 접근법&lt;/li&gt;
&lt;li&gt;한 프롬프트의 출력이 다음 프롬프트의 입력으로 사용되는 파이프라인 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 핵심 원리&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문제 분해(Decomposition): 복잡한 작업을 독립적인 하위 작업으로 분할&lt;/li&gt;
&lt;li&gt;순차적 처리(Sequential Processing): 각 단계를 순서대로 처리하여 최종 결과 도출&lt;/li&gt;
&lt;li&gt;피드백 루프(Feedback Loop): 필요시 이전 단계로 돌아가 수정 및 개선 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 주요 장점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;다단계 작업 처리 강화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;조사, 계획, 작성 등 여러 단계가 필요한 작업에서 고품질 결과 보장&lt;/li&gt;
&lt;li&gt;각 단계별 최적화된 프롬프트 설계 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;복잡한 지시사항 관리 개선&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LLM이 한 번에 처리하기 어려운 복잡한 작업을 단계적으로 분해&lt;/li&gt;
&lt;li&gt;각 단계의 작업 성능을 독립적으로 향상 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;출력물 검증 및 품질 향상&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;중간 결과를 검증하고 필터링하는 단계 추가 가능&lt;/li&gt;
&lt;li&gt;최종 출력물의 안전성, 정확성, 관련성 향상&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;병렬 처리 효율성&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;독립적인 하위 작업을 병렬로 처리해 시간 절약&lt;/li&gt;
&lt;li&gt;리소스 활용 최적화 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) 구현 예시: 연구 논문 작성 지원&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;# 단계 1: 주제 분석 및 정의
프롬프트: &quot;인공지능 윤리에 관한 연구 주제를 구체화해 주세요. 주요 연구 질문 3개를 제안하세요.&quot;
출력: [연구 질문 리스트]

# 단계 2: 문헌 조사 가이드
프롬프트: &quot;다음 연구 질문에 대한 문헌 조사를 위해 중요한 키워드와 검색 전략을 제안해 주세요: [단계 1 출력]&quot;
출력: [키워드 및 검색 전략]

# 단계 3: 개요 작성
프롬프트: &quot;다음 연구 질문과 키워드를 바탕으로 연구 논문의 상세 개요를 작성해 주세요: [단계 1, 2 출력]&quot;
출력: [논문 개요]

# 단계 4: 각 섹션 작성
프롬프트: &quot;다음 개요의 '방법론' 섹션을 상세히 작성해 주세요: [단계 3 출력]&quot;
출력: [방법론 섹션 내용]

# 단계 5: 검토 및 개선
프롬프트: &quot;다음 '방법론' 섹션을 검토하고 개선점을 제안해 주세요: [단계 4 출력]&quot;
출력: [개선된 방법론 섹션]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5) 활용 영역&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;복잡한 창작 작업 (에세이, 기사, 소설 등)&lt;/li&gt;
&lt;li&gt;다단계 분석 및 의사결정 프로세스&lt;/li&gt;
&lt;li&gt;코드 생성 및 디버깅&lt;/li&gt;
&lt;li&gt;복잡한 정보 합성 및 요약&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6) 구현 시 고려사항&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;적절한 작업 분해 수준 결정&lt;/li&gt;
&lt;li&gt;각 단계 간 정보 전달의 일관성 유지&lt;/li&gt;
&lt;li&gt;에러 전파 방지 메커니즘 구축&lt;/li&gt;
&lt;li&gt;전체 파이프라인의 효율성과 비용 균형&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. Tree of Thoughts (ToT): 전략적 탐색을 통한 문제 해결&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 개념&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Chain-of-Thought를 확장한 보다 전략적인 문제 해결 프레임워크&lt;/li&gt;
&lt;li&gt;여러 가능한 사고 경로를 트리 구조로 탐색하는 접근법&lt;/li&gt;
&lt;li&gt;문제 해결 과정에서 다양한 가능성을 탐색하고 평가하여 최적의 해결책 도출&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) CoT와의 차이점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CoT는 선형적(Linear)으로 한 사고 경로만 따라가는 반면, ToT는 다양한 경로를 동시에 탐색&lt;/li&gt;
&lt;li&gt;ToT는 중간에 회귀하며 더 유망한 경로를 시도하는 적응적 탐색 방식 채택&lt;/li&gt;
&lt;li&gt;문제 해결의 각 단계에서 여러 대안을 평가하고 선택하는 의사결정 트리 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 4단계 작동 과정&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;1단계: 생각 분해(Thought Decomposition)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문제를 여러 중간 단계로 나누어 구조화&lt;/li&gt;
&lt;li&gt;각 단계에서 필요한 의사결정 포인트 식별&lt;/li&gt;
&lt;li&gt;문제 해결을 위한 전략적 계획 수립&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;2단계: 생각 생성(Thought Generation)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 단계에서 여러 가능한 &quot;생각&quot;(사고 경로) 생성&lt;/li&gt;
&lt;li&gt;다양한 접근법과 가능성 탐색&lt;/li&gt;
&lt;li&gt;창의적이고 다각적인 해결책 모색&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;3단계: 생각 평가(Thought Evaluation)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생성된 각 생각의 유망성과 타당성 평가&lt;/li&gt;
&lt;li&gt;문제 해결 가능성과 효율성 기준으로 점수 부여&lt;/li&gt;
&lt;li&gt;가장 유망한 사고 경로 식별&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;4단계: 검색 알고리즘(Search Strategy)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;깊이 우선 탐색(DFS) 또는 너비 우선 탐색(BFS) 전략 적용&lt;/li&gt;
&lt;li&gt;가장 유망한 경로를 우선적으로 탐색하는 전략적 접근&lt;/li&gt;
&lt;li&gt;필요시 백트래킹을 통한 대안 경로 탐색&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고급 프롬프트 엔지니어링 기법들은 LLM의 능력을 한 단계 더 끌어올리는 중요한 도구입니다. Generate Knowledge Prompting은 지식 기반 추론을, Prompt Chaining은 복잡한 작업의 단계적 분해를, Tree of Thoughts는 전략적 문제 해결 능력을 강화합니다. 이러한 고급 기법들을 적절히 활용하면 더 정확하고, 신뢰할 수 있으며, 복잡한 작업도 효과적으로 수행할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;패스트 캠퍼스 - 국내 공채 1호 프롬프트 엔지니어 강수진의 프롬프트 엔지니어링 A to Z&lt;/p&gt;</description>
      <category>LLM Engineering/LLMops</category>
      <category>prompt</category>
      <category>prompt engineering</category>
      <category>tot</category>
      <category>프롬프트 엔지니어링</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/105</guid>
      <comments>https://striver.tistory.com/entry/Prompt-Engineering-%ED%94%84%EB%A1%AC%ED%94%84%ED%8A%B8-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4%EB%A7%81%EC%9D%98-%EC%8B%AC%ED%99%94-%EA%B8%B0%EB%B2%95%EB%93%A41#entry105comment</comments>
      <pubDate>Wed, 30 Apr 2025 22:03:49 +0900</pubDate>
    </item>
    <item>
      <title>[Prompt Engineering] 프롬프트 엔지니어링의 기초 기법들</title>
      <link>https://striver.tistory.com/entry/%ED%94%84%EB%A1%AC%ED%94%84%ED%8A%B8-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4%EB%A7%81%EC%9D%98-%EA%B8%B0%EC%B4%88-%EA%B8%B0%EB%B2%95%EB%93%A4</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프롬프트 엔지니어링은 대규모 언어 모델(LLM)의 잠재력을 최대한 활용하기 위한 중요한 기술입니다. 적절한 프롬프트 기법을 사용하면 복잡한 문제 해결, 정확한 정보 검색, 창의적인 콘텐츠 생성 등 다양한 작업에서 더 나은 결과를 얻을 수 있습니다. 이 글에서는 프롬프트 엔지니어링의 기본 기법들을 소개하고, 각 기법의 장단점과 활용 사례를 살펴보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. Zero-shot Prompting: 기본 중의 기본&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 개념&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;언어 모델에게 예제나 시연 없이 직접 작업을 지시하는 방법&lt;/li&gt;
&lt;li&gt;모델이 사전 학습 과정에서 습득한 지식을 바탕으로 작업을 수행&lt;/li&gt;
&lt;li&gt;가장 단순하고 직관적인 프롬프트 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 작동 원리&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LLM은 대량의 텍스트 데이터로 사전 학습되어 있어 다양한 지식을 내포&lt;/li&gt;
&lt;li&gt;명확한 지시만으로도 기본적인 작업을 수행할 수 있음&lt;/li&gt;
&lt;li&gt;모델의 파라미터 크기가 클수록 Zero-shot 성능이 향상됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 적용 사례&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;텍스트 분류: &quot;다음 리뷰가 긍정적인지 부정적인지 분류해주세요.&quot;&lt;/li&gt;
&lt;li&gt;번역: &quot;다음 문장을 영어로 번역해주세요.&quot;&lt;/li&gt;
&lt;li&gt;질문 답변: &quot;인공지능의 정의는 무엇인가요?&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) 한계점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;복잡한 추론이 필요한 작업에서는 성능이 제한적&lt;/li&gt;
&lt;li&gt;특수 도메인 지식이 필요한 작업에서 부정확할 수 있음&lt;/li&gt;
&lt;li&gt;모델이 작업을 정확히 이해하지 못할 경우 관련 없는 답변 생성 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. Few-shot Prompting: 예시를 통한 학습&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 개념&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;언어 모델에게 몇 가지 예시를 제공한 후 유사한 패턴의 작업을 요청하는 방법&lt;/li&gt;
&lt;li&gt;복잡하거나 특수한 작업에서 Zero-shot의 한계를 극복&lt;/li&gt;
&lt;li&gt;&quot;in-context learning&quot;(문맥 내 학습)의 대표적인 형태&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 작동 원리&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력-출력 쌍의 예시를 통해 모델에게 작업의 패턴을 보여줌&lt;/li&gt;
&lt;li&gt;모델이 제공된 예시의 패턴을 파악하고 새로운 입력에 적용&lt;/li&gt;
&lt;li&gt;예시의 개수, 품질, 다양성이 성능에 영향을 미침&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 예시 구성 방법&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력-출력 쌍을 명확하게 구분하여 제시&lt;/li&gt;
&lt;li&gt;다양한 케이스를 포함하여 모델의 이해도 향상&lt;/li&gt;
&lt;li&gt;실제 문제와 유사한 난이도와 형식의 예시 선택&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;입력: 이 영화는 정말 재미있었어요!
출력: 긍정

입력: 시간 낭비였습니다.
출력: 부정

입력: 음식은 괜찮았지만 서비스가 아쉬웠어요.
출력: [여기에 답변 생성]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) 효과적인 활용 영역&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특수 형식의 데이터 변환&lt;/li&gt;
&lt;li&gt;특정 스타일의 텍스트 생성&lt;/li&gt;
&lt;li&gt;복잡한 분류 작업&lt;/li&gt;
&lt;li&gt;도메인 특화 작업&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5) 한계점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;토큰 제한으로 인해 많은 예시를 제공하기 어려움&lt;/li&gt;
&lt;li&gt;예시 선택의 편향이 결과에 영향을 미칠 수 있음&lt;/li&gt;
&lt;li&gt;예시 준비에 시간과 노력이 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. Chain-of-Thought (CoT)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 개념&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;복잡한 추론 과제에서 중간 사고 과정을 단계별로 보여주는 방법&lt;/li&gt;
&lt;li&gt;&quot;Let's solve this step by step&quot;과 같은 사고 유도 문구 활용&lt;/li&gt;
&lt;li&gt;복잡한 수학 문제, 논리 퍼즐, 다단계 추론 작업에 효과적&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 작동 원리&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델에게 문제 해결을 위한 중간 단계를 명시적으로 생성하도록 유도&lt;/li&gt;
&lt;li&gt;단계별 추론을 통해 최종 답변의 정확도 향상&lt;/li&gt;
&lt;li&gt;모델이 문제를 분해하고 체계적으로 접근하도록 도움&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) Few-shot CoT 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;문제: 영희는 사과 5개를 가지고 있었습니다. 철수에게 2개를 주고, 민수에게서 3개를 받았습니다. 영희는 사과를 몇 개 가지고 있나요?

풀이:
1. 영희의 초기 사과 개수: 5개
2. 철수에게 준 사과: 2개
3. 영희에게 남은 사과: 5 - 2 = 3개
4. 민수에게서 받은 사과: 3개
5. 영희의 최종 사과 개수: 3 + 3 = 6개
답: 6개

문제: [새로운 문제]
풀이:
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) 효과적인 활용 영역&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수학 문제 해결&lt;/li&gt;
&lt;li&gt;논리 퍼즐&lt;/li&gt;
&lt;li&gt;다단계 계획 수립&lt;/li&gt;
&lt;li&gt;복잡한 분석이 필요한 질문&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5) 한계점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작은 규모의 모델에서는 효과가 제한적&lt;/li&gt;
&lt;li&gt;사고 과정 예시 작성에 전문성이 필요&lt;/li&gt;
&lt;li&gt;프롬프트 길이가 길어져 토큰 소비 증가&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4. Zero-shot Chain of Thought&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 개념&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CoT의 장점을 유지하면서 예시 작성의 번거로움을 해결하는 방법&lt;/li&gt;
&lt;li&gt;&quot;Let's think step by step&quot;과 같은 간단한 지시문만으로 단계적 사고 유도&lt;/li&gt;
&lt;li&gt;최소한의 프롬프트로 복잡한 추론 능력 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 작동 원리&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델에게 단계적 사고를 명시적으로 요청&lt;/li&gt;
&lt;li&gt;모델이 스스로 문제 해결 단계를 생성하고 따라가도록 유도&lt;/li&gt;
&lt;li&gt;사고 과정의 외재화를 통한 오류 감소&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 프롬프트 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;문제: 한 상자에 빨간 공 3개, 파란 공 5개, 녹색 공 2개가 있습니다. 무작위로 공 2개를 동시에 꺼낼 때, 두 공의 색이 서로 다를 확률은?

단계적으로 생각해봅시다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) 효과와 활용&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;간결한 프롬프트로 복잡한 추론 유도&lt;/li&gt;
&lt;li&gt;다양한 문제 유형에 범용적으로 적용 가능&lt;/li&gt;
&lt;li&gt;최소한의 맥락으로 효율적인 토큰 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5) 한계점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일부 복잡한 문제에서는 Few-shot CoT보다 성능이 낮을 수 있음&lt;/li&gt;
&lt;li&gt;모델의 기본 능력에 의존하므로 모델 성능에 따라 결과 차이가 큼&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;5. Self-Consistency&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 개념&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CoT를 확장하여 여러 추론 경로를 생성한 후 가장 일관된 답변 선택&lt;/li&gt;
&lt;li&gt;다양한 접근 방식을 통해 단일 추론의 오류 가능성 감소&lt;/li&gt;
&lt;li&gt;집단 지성(wisdom of crowds) 원리를 LLM에 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 작동 과정&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동일한 문제에 대해 여러 CoT 추론 경로 생성 (샘플링)&lt;/li&gt;
&lt;li&gt;각 경로에서 도출된 답변들을 수집&lt;/li&gt;
&lt;li&gt;가장 많이 도출된 답변(다수결)을 최종 결과로 선택&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 구현 방식&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;온도(temperature) 설정을 높여 다양한 추론 경로 생성&lt;/li&gt;
&lt;li&gt;각 추론에서 도출된 최종 답변 추출 및 집계&lt;/li&gt;
&lt;li&gt;가장 빈도가 높은 답변 또는 확률적으로 가장 유력한 답변 선택&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) 효과적인 활용 영역&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;고난도 수학 문제&lt;/li&gt;
&lt;li&gt;불확실성이 높은 추론 작업&lt;/li&gt;
&lt;li&gt;정확도가 중요한 의사 결정 시나리오&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5) 한계점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 추론 경로 생성으로 인한 계산 비용 증가&lt;/li&gt;
&lt;li&gt;구현 복잡성이 높아 실용적 적용이 어려울 수 있음&lt;/li&gt;
&lt;li&gt;일부 문제에서는 다수결이 항상 최적 답변을 보장하지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;6. Generated Knowledge (GoT)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 개념&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델이 문제 해결에 필요한 배경 지식을 먼저 생성한 후 추론하는 방법&lt;/li&gt;
&lt;li&gt;복잡한 추론 전에 관련 사실, 개념, 원리를 명시적으로 정리&lt;/li&gt;
&lt;li&gt;지식 생성과 추론을 분리하여 더 정확한 결과 도출&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 작동 과정&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문제와 관련된 배경 지식, 개념, 원리 생성&lt;/li&gt;
&lt;li&gt;생성된 지식을 바탕으로 CoT 추론 수행&lt;/li&gt;
&lt;li&gt;필요시 추가 지식 생성과 추론을 반복&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 프롬프트 구조 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;문제: [문제 설명]

1. 먼저 이 문제를 해결하는 데 필요한 관련 지식을 정리해봅시다:
[지식 생성 공간]

2. 위 지식을 바탕으로 문제를 단계적으로 해결해봅시다:
[추론 공간]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) 효과적인 활용 영역&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특수 도메인 지식이 필요한 문제&lt;/li&gt;
&lt;li&gt;개념 이해가 중요한 교육적 맥락&lt;/li&gt;
&lt;li&gt;복잡한 과학/수학 문제&lt;/li&gt;
&lt;li&gt;전문 분야의 질문-답변&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5) 장점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;관련 지식의 명시적 활성화로 정확도 향상&lt;/li&gt;
&lt;li&gt;추론 과정의 투명성 증가&lt;/li&gt;
&lt;li&gt;지식 부족으로 인한 오류 감소&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6) 한계점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생성된 지식의 정확성에 의존&lt;/li&gt;
&lt;li&gt;프롬프트 길이와 복잡성 증가&lt;/li&gt;
&lt;li&gt;일부 간단한 문제에서는 불필요한 오버헤드 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프롬프트 엔지니어링은 LLM의 잠재력을 최대한 활용하기 위한 핵심 기술로, 지속적으로 발전하고 있습니다. 기본 기법들을 이해하고 효과적으로 적용함으로써, 복잡한 문제 해결부터 창의적인 콘텐츠 생성까지 다양한 작업에서 AI의 능력을 크게 향상시킬 수 있습니다. 각 기법의 장단점을 파악하고 상황에 맞게 활용하는 것이 프롬프트 엔지니어링의 핵심입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패스트 캠퍼스 - 국내 공채 1호 프롬프트 엔지니어 강수진의 프롬프트 엔지니어링 A to Z&lt;/p&gt;</description>
      <category>LLM Engineering/LLMops</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/104</guid>
      <comments>https://striver.tistory.com/entry/%ED%94%84%EB%A1%AC%ED%94%84%ED%8A%B8-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4%EB%A7%81%EC%9D%98-%EA%B8%B0%EC%B4%88-%EA%B8%B0%EB%B2%95%EB%93%A4#entry104comment</comments>
      <pubDate>Tue, 29 Apr 2025 22:16:07 +0900</pubDate>
    </item>
    <item>
      <title>[DeepLearning] 딥러닝 프레임워크 비교 분석</title>
      <link>https://striver.tistory.com/entry/DeepLearning-%EB%94%A5%EB%9F%AC%EB%8B%9D-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 주요 딥러닝 프레임워크 소개&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) TensorFlow&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TensorFlow는 구글에서 개발한 오픈 소스 머신러닝 프레임워크로, 대규모 데이터와 복잡한 모델을 효율적으로 처리할 수 있습니다. CPU와 GPU를 모두 지원하며, TensorBoard라는 시각화 도구를 통해 학습 과정을 모니터링할 수 있습니다. 주로 대규모 프로젝트와 연구에서 활용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) PyTorch&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PyTorch는 페이스북이 개발한 프레임워크로, 동적 계산 그래프를 특징으로 합니다. 직관적인 API와 사용 편의성으로 연구자들 사이에서 큰 인기를 얻고 있으며, 특히 자연어 처리(NLP)와 컴퓨터 비전 분야에서 널리 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3) Keras&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Keras는 사용자 친화적인 고수준 API로, 여러 백엔드(TensorFlow, Theano, CNTK)를 지원합니다. 직관적인 인터페이스로 초보자도 쉽게 신경망을 구축할 수 있어, 빠른 프로토타이핑과 소규모 연구에 적합합니다. 현재는 TensorFlow에 통합되어 제공됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;4) Scikit-learn&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Scikit-learn은 파이썬 기반의 머신러닝 라이브러리로, 다양한 머신러닝 알고리즘을 제공합니다. 데이터 전처리, 분류, 회귀, 군집화 등 다양한 기능을 포함하며, NumPy, SciPy와 통합되어 강력한 데이터 처리 능력을 제공합니다. 교육용과 중소규모 프로젝트에 적합합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 프레임워크 특징 비교&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) 성능 및 확장성&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TensorFlow: 대규모 데이터셋과 복잡한 모델에 최적화되어 있으며, 분산 학습을 잘 지원합니다.&lt;/li&gt;
&lt;li&gt;PyTorch: 동적 계산 그래프로 유연한 모델 구현이 가능하며, 디버깅이 용이합니다.&lt;/li&gt;
&lt;li&gt;Keras: 간결한 코드로 빠르게 모델을 구축할 수 있지만, 매우 복잡한 모델에는 제한이 있습니다.&lt;/li&gt;
&lt;li&gt;Scikit-learn: 전통적인 머신러닝 알고리즘에 강점을 보이지만, 딥러닝에는 제한적입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) 사용 편의성&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TensorFlow: 다양한 기능을 제공하지만, 상대적으로 학습 곡선이 가파릅니다.&lt;/li&gt;
&lt;li&gt;PyTorch: 파이썬 스타일의 직관적인 인터페이스로 빠르게 익힐 수 있습니다.&lt;/li&gt;
&lt;li&gt;Keras: 가장 사용자 친화적인 인터페이스를 제공하여 초보자에게 이상적입니다.&lt;/li&gt;
&lt;li&gt;Scikit-learn: 일관된 API로 쉽게 다양한 알고리즘을 적용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3) 커뮤니티 및 지원&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TensorFlow: 대규모 커뮤니티와 풍부한 문서, 튜토리얼을 보유하고 있습니다.&lt;/li&gt;
&lt;li&gt;PyTorch: 급속도로 성장하는 커뮤니티와 활발한 연구 지원을 받고 있습니다.&lt;/li&gt;
&lt;li&gt;Keras: TensorFlow의 공식 고수준 API로 통합되어 강력한 지원을 받습니다.&lt;/li&gt;
&lt;li&gt;Scikit-learn: 안정적인 커뮤니티와 잘 정리된 문서를 가지고 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 활용 사례별 최적 프레임워크&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) 연구 및 프로토타이핑&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;추천: PyTorch, Keras&lt;/li&gt;
&lt;li&gt;이유: 동적 계산 그래프(PyTorch)와 빠른 모델 구현(Keras)이 실험과 반복에 적합합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) 대규모 프로덕션 환경&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;추천: TensorFlow&lt;/li&gt;
&lt;li&gt;이유: TensorFlow Serving, TensorFlow Lite 등 배포 도구와 최적화 기능이 우수합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3) 교육 및 학습&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;추천: Keras, Scikit-learn&lt;/li&gt;
&lt;li&gt;이유: 간결한 문법과 직관적인 인터페이스로 개념 이해에 집중할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;4) 특정 응용 분야&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴퓨터 비전: PyTorch, TensorFlow&lt;/li&gt;
&lt;li&gt;자연어 처리: PyTorch(특히 최신 연구에서), TensorFlow&lt;/li&gt;
&lt;li&gt;일반 머신러닝: Scikit-learn&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4. 프레임워크 선택 가이드&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) 고려 사항&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로젝트 규모와 복잡성&lt;/li&gt;
&lt;li&gt;사용자의 경험 수준&lt;/li&gt;
&lt;li&gt;특정 응용 분야의 요구사항&lt;/li&gt;
&lt;li&gt;배포 환경 및 운영 요구사항&lt;/li&gt;
&lt;li&gt;커뮤니티 지원 및 문서화 수준&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) 선택 시나리오&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초보자가 딥러닝을 배우려는 경우: Keras로 시작하여 기본 개념 습득&lt;/li&gt;
&lt;li&gt;연구자가 최신 모델을 실험하는 경우: PyTorch의 유연성 활용&lt;/li&gt;
&lt;li&gt;기업에서 안정적인 서비스를 개발하는 경우: TensorFlow의 생태계 활용&lt;/li&gt;
&lt;li&gt;전통적인 머신러닝 작업이 필요한 경우: Scikit-learn의 다양한 알고리즘 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;5. 결론&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딥러닝 프레임워크는 각각 고유한 강점과 약점을 가지고 있습니다. TensorFlow는 대규모 배포와 프로덕션에, PyTorch는 연구와 실험에, Keras는 빠른 프로토타이핑과 교육에, Scikit-learn은 전통적인 머신러닝 작업에 적합합니다. 프로젝트의 특성과 목표에 맞는 프레임워크를 선택하는 것이 성공적인 머신러닝/딥러닝 개발의 첫 단계입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://wikidocs.net/250786&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://wikidocs.net/250786&lt;/a&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>AI/DeepLearning</category>
      <category>Keras</category>
      <category>pytorch</category>
      <category>scikit-learn</category>
      <category>TensorFlow</category>
      <category>딥러닝 프레임워크</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/102</guid>
      <comments>https://striver.tistory.com/entry/DeepLearning-%EB%94%A5%EB%9F%AC%EB%8B%9D-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC#entry102comment</comments>
      <pubDate>Fri, 25 Apr 2025 23:02:07 +0900</pubDate>
    </item>
    <item>
      <title>[Knowledge] 학습 파라미터, 트레이너 종류, GPU 인프라 총정리</title>
      <link>https://striver.tistory.com/entry/Knowledge-%ED%95%99%EC%8A%B5-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%ED%8A%B8%EB%A0%88%EC%9D%B4%EB%84%88-%EC%A2%85%EB%A5%98-GPU-%EC%9D%B8%ED%94%84%EB%9D%BC-%EC%B4%9D%EC%A0%95%EB%A6%AC</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;최근 학습 과정에서 생겼던 궁금증에 대해 간략하게 정리해보는 시간을 가졌습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 학습 파라미터 vs 하이퍼 파라미터&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) 학습 파라미터&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학습 파라미터는 모델이 학습 과정에서 스스로 최적화하는 내부 변수들입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정의&lt;/b&gt;: 데이터로부터 학습을 통해 자동으로 업데이트되는 모델 내부 변수&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예시&lt;/b&gt;: 신경망의 가중치(weights), 편향(biases), 임베딩(embeddings)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;특징&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;역전파를 통해 자동으로 조정됨&lt;/li&gt;
&lt;li&gt;모델의 성능을 직접적으로 결정하는 요소&lt;/li&gt;
&lt;li&gt;학습이 완료된 후 모델과 함께 저장됨&lt;/li&gt;
&lt;li&gt;파인튜닝 시 주로 조정되는 대상&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대규모 언어 모델(LLM)의 경우, 학습 파라미터는 수십억 개에 달할 수 있으며, 파인튜닝 과정에서는 이 파라미터의 일부 또는 전체를 조정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) 하이퍼 파라미터&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하이퍼파라미터는 모델의 학습 방식을 제어하는 외부 설정값입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정의&lt;/b&gt;: 학습 과정을 제어하기 위해 사람이 사전에 설정하는 변수들&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예시&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;학습률(learning rate)&lt;/li&gt;
&lt;li&gt;배치 크기(batch size)&lt;/li&gt;
&lt;li&gt;에폭 수(epochs)&lt;/li&gt;
&lt;li&gt;정규화 계수(regularization factor)&lt;/li&gt;
&lt;li&gt;드롭아웃 비율(dropout rate)&lt;/li&gt;
&lt;li&gt;모델 구조 설정(은닉층 수, 뉴런 수)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;특징&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델이 자동으로 학습하지 않으며 개발자가 직접 설정&lt;/li&gt;
&lt;li&gt;학습 프로세스와 속도, 성능에 큰 영향을 미침&lt;/li&gt;
&lt;li&gt;최적값을 찾기 위해 그리드 서치(Grid Search), 랜덤 서치(Random Search), 베이지안 최적화(Bayesian Optimization) 등의 방법 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3) 실제 코드에서의 구분&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1745496759750&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 학습 파라미터 (모델 내부에서 자동으로 학습됨)
# q_proj, k_proj, v_proj, o_proj, gate_proj 등의 가중치
model = FastLanguageModel.get_peft_model(
    model,
    r=128,  # Choose any number &amp;gt; 0 ! Suggested 8, 16, 32, 64, 128
    target_modules=[
        &quot;q_proj&quot;,
        &quot;k_proj&quot;,
        &quot;v_proj&quot;,
        &quot;o_proj&quot;,
        &quot;gate_proj&quot;,
        &quot;up_proj&quot;,
        &quot;down_proj&quot;,
        &quot;embed_tokens&quot;,
        &quot;lm_head&quot;,
    ],  # Add for continual pretraining
    lora_alpha=32,
    lora_dropout=0,  # Supports any, but = 0 is optimized
    bias=&quot;none&quot;,  # Supports any, but = &quot;none&quot; is optimized
    # [NEW] &quot;unsloth&quot; uses 30% less VRAM, fits 2x larger batch sizes!
    use_gradient_checkpointing=&quot;unsloth&quot;,  # True or &quot;unsloth&quot; for very long context
    random_state=3407,
    use_rslora=True,  # We support rank stabilized LoRA
    loftq_config=None,  # And LoftQ
)

# 하이퍼파라미터 (개발자가 직접 설정)
args = UnslothTrainingArguments(
    per_device_train_batch_size=2,  # 배치 크기 설정
    gradient_accumulation_steps=8,  # 그래디언트 누적 단계 수
    warmup_steps=10,                # 워밍업 스텝 수
    num_train_epochs=1,             # 학습 에폭 수
    learning_rate=5e-5,             # 학습률
    embedding_learning_rate=1e-5,   # 임베딩 학습률
    fp16=True,                      # 16비트 부동소수점 사용 여부
    bf16=False,                     # bfloat16 사용 여부
    # ... 기타 설정들
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. Trainer 종류 및 장단점&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) Hugging Face Trainer&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hugging Face의 기본 트레이너로, 다양한 모델과 데이터셋에 범용적으로 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장점:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쉬운 사용법과 포괄적인 문서화&lt;/li&gt;
&lt;li&gt;다양한 모델 아키텍처 지원&lt;/li&gt;
&lt;li&gt;분산 학습, 혼합 정밀도 학습 등 고급 기능 내장&lt;/li&gt;
&lt;li&gt;커스터마이징이 용이한 콜백 시스템&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;단점:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대규모 LLM 파인튜닝에 최적화되어 있지 않음&lt;/li&gt;
&lt;li&gt;메모리 사용량이 다소 높을 수 있음&lt;/li&gt;
&lt;li&gt;특수한 학습 기법(PEFT, QLoRA 등)에 대한 직접적인 지원이 부족&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) UnslothTrainer&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unsloth 라이브러리의 트레이너로, 대규모 LLM의 효율적인 파인튜닝에 특화되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장점:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LLM 파인튜닝에 최적화된 성능&lt;/li&gt;
&lt;li&gt;메모리 효율성이 높아 더 큰 배치 크기 사용 가능&lt;/li&gt;
&lt;li&gt;FlashAttention, LoRA와 같은 최신 기술 내장&lt;/li&gt;
&lt;li&gt;학습 속도가 일반 Trainer보다 빠름 (2-3배 속도 향상)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;단점:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;범용성이 다소 부족 (주로 LLaMA, Mistral 계열 모델에 최적화)&lt;/li&gt;
&lt;li&gt;상대적으로 새로운 라이브러리로 문서화가 덜 완성됨&lt;/li&gt;
&lt;li&gt;커스터마이징 옵션이 제한적일 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3) SFTTrainer&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TRL 라이브러리의 일부로, 지시어 튜닝과 같은 지도 학습 파인튜닝에 특화되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장점:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대화형 AI 모델 학습에 최적화&lt;/li&gt;
&lt;li&gt;지시어 형식의 데이터 처리가 간편함&lt;/li&gt;
&lt;li&gt;RLHF(Reinforcement Learning from Human Feedback) 파이프라인과 연동 가능&lt;/li&gt;
&lt;li&gt;토크나이징 프로세스가 자동화되어 사용이 편리함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;단점:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반적인 분류나 회귀 태스크에는 덜 적합&lt;/li&gt;
&lt;li&gt;학습 과정의 세밀한 제어가 다소 제한적&lt;/li&gt;
&lt;li&gt;특화된 용도로 인해 범용성이 다소 떨어짐&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. GPU 인스턴스 종류&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) NVIDIA A100&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사양:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리: 40GB / 80GB 버전 존재&lt;/li&gt;
&lt;li&gt;FP32 성능: 최대 19.5 TFLOPS&lt;/li&gt;
&lt;li&gt;FP16/BF16 성능: 최대 312 TFLOPS (Tensor Cores)&lt;/li&gt;
&lt;li&gt;메모리 대역폭: 1.6 TB/s (HBM2e)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장점:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;우수한 가격 대비 성능&lt;/li&gt;
&lt;li&gt;충분한 메모리로 대규모 모델 학습 가능&lt;/li&gt;
&lt;li&gt;다양한 클라우드 제공업체에서 폭넓게 사용 가능&lt;/li&gt;
&lt;li&gt;TensorFloat-32(TF32) 지원으로 학습 속도 향상&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;단점:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;H100보다는 성능이 떨어짐&lt;/li&gt;
&lt;li&gt;가격이 여전히 높은 편 (특히 80GB 버전)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;적합한 사용 사례:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;7B-70B 파라미터 규모의 LLM 파인튜닝&lt;/li&gt;
&lt;li&gt;중간 규모의 연구 및 개발 프로젝트&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) NVIDIA H100&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사양:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리: 80GB&lt;/li&gt;
&lt;li&gt;FP32 성능: 최대 67 TFLOPS&lt;/li&gt;
&lt;li&gt;FP16/BF16 성능: 최대 1,979 TFLOPS (Tensor Cores)&lt;/li&gt;
&lt;li&gt;메모리 대역폭: 3.35 TB/s (HBM3)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장점:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최고 수준의 연산 성능&lt;/li&gt;
&lt;li&gt;대규모 모델 학습 및 추론에 뛰어난 성능&lt;/li&gt;
&lt;li&gt;향상된 메모리 대역폭으로 병목 현상 감소&lt;/li&gt;
&lt;li&gt;Transformer 엔진으로 LLM 학습 최적화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;단점:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;매우 높은 비용&lt;/li&gt;
&lt;li&gt;가용성이 제한적일 수 있음&lt;/li&gt;
&lt;li&gt;냉각 요구사항이 더 높음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;적합한 사용 사례:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;100B+ 파라미터 규모의 대형 LLM 파인튜닝&lt;/li&gt;
&lt;li&gt;최고 성능이 필요한 대규모 상업 프로젝트&lt;/li&gt;
&lt;li&gt;다중 GPU 환경에서의 분산 학습&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3) 기타 주요 GPU 옵션&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;NVIDIA V100&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A100의 이전 세대로, 여전히 많이 사용됨&lt;/li&gt;
&lt;li&gt;16GB/32GB 메모리 옵션&lt;/li&gt;
&lt;li&gt;비용 효율적인 대안이지만 최신 최적화 부족&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;NVIDIA T4&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;저비용 추론 특화 GPU&lt;/li&gt;
&lt;li&gt;파인튜닝에는 제한적이나 가벼운 작업에 적합&lt;/li&gt;
&lt;li&gt;16GB 메모리로 소규모 모델 파인튜닝 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;NVIDIA RTX 4090 (소비자용)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;성능 대비 가격이 우수함&lt;/li&gt;
&lt;li&gt;24GB 메모리로 중소형 모델 파인튜닝 가능&lt;/li&gt;
&lt;li&gt;대규모 프로젝트에는 부적합&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AMD MI250X&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;NVIDIA 대안으로 부상 중&lt;/li&gt;
&lt;li&gt;LLM 학습 생태계 지원이 상대적으로 부족&lt;/li&gt;
&lt;li&gt;특정 워크로드에서 좋은 성능&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>AI/DeepLearning</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/101</guid>
      <comments>https://striver.tistory.com/entry/Knowledge-%ED%95%99%EC%8A%B5-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%ED%8A%B8%EB%A0%88%EC%9D%B4%EB%84%88-%EC%A2%85%EB%A5%98-GPU-%EC%9D%B8%ED%94%84%EB%9D%BC-%EC%B4%9D%EC%A0%95%EB%A6%AC#entry101comment</comments>
      <pubDate>Thu, 24 Apr 2025 21:23:07 +0900</pubDate>
    </item>
    <item>
      <title>[Fine-Tuning] Restoring obfuscation LLM 프로젝트 회고</title>
      <link>https://striver.tistory.com/entry/Fine-Tuning-Restoring-obfuscation-LLM-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전 글에서 Unsloth와 엘리스 클라우드 등 기술적인 부분에 대해 설명했다면, 이번에는 DACON 난독화 한글 리뷰 복원 프로젝트를 진행하면서 겪었던 전체적인 경험과 시행착오를 공유하려 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;대회 개요&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DACON에서 진행된 이 대회는 의도적으로 난독화된 한글 텍스트를 원래 형태로 복원하는 과제로, 오픈 소스 LLM(Large Language Model)을 활용해 해결하는 것이 핵심이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 같은 경진대회에 참여한 사람이 공유해준 Gemma-2-B-it Full Fint-tuning 모델을 사용하였습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745478364602&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;model_name = &quot;mindw96/Gemma-2-2B-it-DACON-LLM&quot;
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype=torch.bfloat16,
    device_map=&quot;auto&quot;,
    trust_remote_code=True,
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2B 파라미터 규모의 작은 모델이었지만, Full-Fine-tuning이라서그런지 0.75의 정확도로 생각보다 높은 수치를 보여줬습니다. 하지만 저는 단순히 다른 사람의 모델을 추론에만 사용하는 것이 아니라, 직접 파인튜닝 과정을 경험하고 더 높은 정확도를 달성하고 싶다는 마음으로 대회를 진행하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;도전 과제: 제한된 컴퓨팅 자원&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확도 향상을 위해 2B보다 더 큰 모델로 풀 파인튜닝을 시도했으나, Colab 무료 환경에서는 지속적으로 OOM(Out Of Memory) 에러가 발생했습니다. 이를 해결하기 위해 배치 사이즈 감소와 양자화 등 여러 메모리 최적화 기법을 시도했지만, 기본적인 메모리가 너무 부족한 상황이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델 크기를 낮추어 학습을 시작했지만, 이번에는 학습 시간이 너무 오래 걸리는 문제에 직면했습니다. 결국 제한된 컴퓨팅 자원과 긴 학습 시간이라는 두 가지 문제를 모두 해결해야 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;해결책: 엘리스 클라우드와 Unsloth 라이브러리&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) 클라우드 환경으로 전환&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근본적인 메모리 부족 문제를 해결하기 위해 엘리스 클라우드의 &quot;A100 80GB PCle MIG 3g-40GB&quot; 인스턴스를 활용하기로 했습니다. 대부분의 연구자들이 RunPod와 같은 플랫폼을 사용하는 경우가 많지만, 엘리스 클라우드는 출시된 지 얼마 되지 않았고 이벤트로 대여 비용을 지원해주어 비용 효율적인 선택이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) Unsloth 라이브러리 도입&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리 사용 효율성과 학습 속도를 개선하기 위해 Unsloth 라이브러리를 활용했습니다. Unsloth는 사전에 통합(integration)된 모델만 지원하므로, Llama-3B 모델을 새롭게 선택하여 학습을 진행했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745478884035&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from unsloth import FastLanguageModel
import torch

# 모델 및 토크나이저 로드
model, tokenizer = FastLanguageModel.from_pretrained(
    model_name=&quot;unsloth/Llama-3.2-3B-Instruct-bnb-4bit&quot;,
    max_seq_length=2048,
    dtype=None,  # 자동 감지
    load_in_4bit=True,  # 4비트 양자화
)

# LoRA 설정으로 효율적인 파인튜닝
model = FastLanguageModel.get_peft_model(
    model,
    r=128,  # LoRA 랭크
    target_modules=[
        &quot;q_proj&quot;, &quot;k_proj&quot;, &quot;v_proj&quot;, &quot;o_proj&quot;, 
        &quot;gate_proj&quot;, &quot;up_proj&quot;, &quot;down_proj&quot;, 
        &quot;embed_tokens&quot;, &quot;lm_head&quot;
    ],
    lora_alpha=32,
    use_gradient_checkpointing=&quot;unsloth&quot;,  # 메모리 효율성
    use_rslora=True,  # 랭크 안정화 LoRA
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후에 학습에 적합한 형태로 데이터를 구성해주는 절차를 거쳤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745479019376&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 난독화된 입력을 복원하는 작업을 위한 템플릿 정의
restore_prompt = &quot;&quot;&quot;다음은 난독화 된 입력을 복원하는 작업입니다.

### 난독화 입력:
{}

### 복원된 출력:
{}&quot;&quot;&quot;

EOS_TOKEN = tokenizer.eos_token  # 반드시 EOS_TOKEN 추가 (생성 종료를 위해)

def combine_input_output(example):
    # 입력과 출력 정보를 템플릿에 맞게 합치고 EOS_TOKEN을 추가
    example[&quot;text&quot;] = (
        restore_prompt.format(example[&quot;input&quot;], example[&quot;output&quot;]) + EOS_TOKEN
    )
    return example


# 데이터셋에 대해 map 적용
dataset = dataset.map(combine_input_output)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 후 UnslothTrainer를 활용해 효율적인 학습을 진행했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745479198800&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;trainer = UnslothTrainer(
    model=model,
    tokenizer=tokenizer,
    train_dataset=dataset,
    dataset_text_field=&quot;text&quot;,
    max_seq_length=2048,
    dataset_num_proc=2,
    args=UnslothTrainingArguments(
        per_device_train_batch_size=2,
        gradient_accumulation_steps=8,
        warmup_steps=10,
        num_train_epochs=1,
        learning_rate=5e-5,
        embedding_learning_rate=1e-5,  # 임베딩 레이어에 더 낮은 학습률 적용
        fp16=not is_bfloat16_supported(),
        bf16=is_bfloat16_supported(),
        logging_steps=1,
        optim=&quot;adamw_8bit&quot;,
        weight_decay=0.01,
        lr_scheduler_type=&quot;linear&quot;,
        output_dir=&quot;outputs&quot;,
    ),
)

# 학습 실행
trainer.train()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;결과 및 성과&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unsloth 라이브러리와 4비트 양자화 기법을 활용한 Llama-3.2-3B 모델 학습으로 &lt;b&gt;0.8의 정확도&lt;/b&gt;를 달성했습니다. 이는 초기 접근법보다 &lt;b&gt;약 6.7% 향상&lt;/b&gt;된 결과입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) 이 접근법의 주요 이점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;메모리 효율성&lt;/b&gt;: 4비트 양자화로 메모리 사용량을 크게 줄임&lt;/li&gt;
&lt;li&gt;&lt;b&gt;학습 속도 향상&lt;/b&gt;: Unsloth의 최적화로 일반적인 방법보다 2-4배 빠른 학습&lt;/li&gt;
&lt;li&gt;&lt;b&gt;유연성&lt;/b&gt;: LoRA를 통해 모든 주요 모듈을 효율적으로 파인튜닝&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;기술적 인사이트&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) LoRA를 이용한 효율적인 파인튜닝&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LoRA(Low-Rank Adaptation)는 전체 모델을 학습하는 대신 저차원 업데이트를 통해 파라미터를 효율적으로 학습합니다. r=128이라는 비교적 큰 랭크 값을 사용해 모델의 표현력을 극대화했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745479362818&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;target_modules=[
    &quot;q_proj&quot;, &quot;k_proj&quot;, &quot;v_proj&quot;, &quot;o_proj&quot;,  # 어텐션 관련
    &quot;gate_proj&quot;, &quot;up_proj&quot;, &quot;down_proj&quot;,     # MLP 관련
    &quot;embed_tokens&quot;, &quot;lm_head&quot;                # 임베딩 및 출력
]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 모든 주요 모듈을 대상으로 함으로써 모델이 난독화 패턴을 더 깊이 이해하고 복원할 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) 4비트 양자화의 효과&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 16비트 형식의 모델을 양자화 하면 메모리 사용량이 줄어드는 대신 정확도가 크게 떨어질것이라고 예상해서 양자화를 선호하지 않았는데, 줄어든 메모리 사용량 대비 성능이 크게 떨어지지않은 것 같아서 양자화가 정확도 손실을 최소화하면서도 큰 모델을 효율적으로 사용하게 해준다는 긍정적인 인식을 얻게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>LLM Engineering/Fine-Tuning</category>
      <category>fine-tuning</category>
      <category>quntization</category>
      <category>unsloth</category>
      <category>양자화</category>
      <category>파인튜닝</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/100</guid>
      <comments>https://striver.tistory.com/entry/Fine-Tuning-Restoring-obfuscation-LLM-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0#entry100comment</comments>
      <pubDate>Wed, 23 Apr 2025 22:17:49 +0900</pubDate>
    </item>
    <item>
      <title>[Agent] ESG 데이터 조회 Agent 구현</title>
      <link>https://striver.tistory.com/entry/Agent-Fuction-calling%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-Agent</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;진행중이던 프로젝트에서 디비를 활용한 부분을 제외하고 API호출을 Tool로 구현하고 LangChain의 Tool calling 기능을 활용하여 ESG 데이터를 조회하는 Agent를 구현한 내용에 대해 말씀드리겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 시스템 아키텍처 개요&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) 구성 요소&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ESG 데이터 수집을 위한 yfinance API 활용&lt;/li&gt;
&lt;li&gt;LangChain의 Tool calling 기능을 통한 자연어 기반 데이터 조회&lt;/li&gt;
&lt;li&gt;ReAct Agent 패턴을 통한 지능형 데이터 분석&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) 주요 흐름&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 자연어로 ESG 데이터 조회 요청&lt;/li&gt;
&lt;li&gt;Agent가 의도를 파악하여 적절한 Tool 호출&lt;/li&gt;
&lt;li&gt;ESG 데이터를 가져와 분석 및 리포트 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. ESG 데이터 조회 Tool 구현&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) ESG Search Wrapper 클래스&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;class ESGSearchWrapper(BaseModel):
    &quot;&quot;&quot;ESG 데이터 검색을 위한 래퍼 클래스.&quot;&quot;&quot;
    
    model_config = ConfigDict(
        extra=&quot;forbid&quot;,
        arbitrary_types_allowed=True,
    )

    def get_from_yfinance_sync(self, ticker: str) -&amp;gt; Dict[str, Any]:
        &quot;&quot;&quot;yfinance API에서 최신 ESG 데이터를 가져옵니다.&quot;&quot;&quot;
        try:
            ticker = ticker.upper()
            ticker_obj = yf.Ticker(ticker)
            esg_data = ticker_obj.sustainability

            if esg_data is None:
                return {
                    &quot;ticker&quot;: ticker,
                    &quot;error&quot;: f&quot;{ticker}에 대한 ESG 데이터를 찾을 수 없습니다.&quot;,
                }

            # DataFrame을 딕셔너리로 변환
            esg_dict = esg_data.to_dict()
            if not esg_dict:
                return {
                    &quot;ticker&quot;: ticker,
                    &quot;error&quot;: f&quot;{ticker}에 대한 ESG 데이터를 찾을 수 없습니다.&quot;,
                }

            # 첫 번째 열 데이터만 추출
            column_name = list(esg_dict.keys())[0]
            esg_values = esg_dict[column_name]

            # 현재 연도와 월 추가
            now = datetime.datetime.now()

            result = {
                &quot;ticker&quot;: ticker,
                &quot;total_esg&quot;: esg_values.get(&quot;totalEsg&quot;),
                &quot;environment_score&quot;: esg_values.get(&quot;environmentScore&quot;),
                &quot;social_score&quot;: esg_values.get(&quot;socialScore&quot;),
                &quot;governance_score&quot;: esg_values.get(&quot;governanceScore&quot;),
                &quot;rating_year&quot;: now.year,
                &quot;rating_month&quot;: now.month,
                &quot;records&quot;: [],
            }

            return result
        except Exception as e:
            return {
                &quot;ticker&quot;: ticker,
                &quot;error&quot;: f&quot;yfinance API 호출 중 오류 발생: {str(e)}&quot;,
            }

    def get_esg_data_sync(self, ticker: str, force_refresh: bool = False) -&amp;gt; Dict[str, Any]:
        &quot;&quot;&quot;주어진 티커에 대한 ESG 데이터를 가져옵니다.&quot;&quot;&quot;
        ticker = ticker.upper()
        return self.get_from_yfinance_sync(ticker)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) ESG Data Tool 클래스&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;class ESGDataInput(BaseModel):
    &quot;&quot;&quot;Input for the ESG Data tool.&quot;&quot;&quot;
    ticker: str = Field(description=&quot;The stock ticker symbol to get ESG data for.&quot;)
    force_refresh: bool = Field(
        description=&quot;Whether to force refresh data from yfinance.&quot;, default=False
    )


class ESGDataTool(BaseTool):
    &quot;&quot;&quot;Tool for retrieving ESG data for stocks.&quot;&quot;&quot;
    
    name: str = &quot;esg_data&quot;
    description: str = (
        &quot;Useful when you need to get ESG (Environmental, Social, Governance) &quot;
        &quot;ratings and scores for publicly traded companies. &quot;
        &quot;Input should be a valid stock ticker symbol.&quot;
    )
    args_schema: Type[BaseModel] = ESGDataInput
    api_wrapper: ESGSearchWrapper = Field(default_factory=ESGSearchWrapper)

    def _run(
        self,
        ticker: str,
        force_refresh: bool = False,
        run_manager: Optional[CallbackManagerForToolRun] = None,
    ) -&amp;gt; Dict[str, Any]:
        &quot;&quot;&quot;Use the tool.&quot;&quot;&quot;
        try:
            result = self.api_wrapper.get_esg_data_sync(
                ticker=ticker,
                force_refresh=force_refresh,
            )
            return result
        except Exception as e:
            return {&quot;error&quot;: repr(e)}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. ReAct Agent 구현&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) ESG 분석 노드&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;smalltalk&quot;&gt;&lt;code&gt;class RetrieveESGNode(Node):
    def __init__(self):
        super().__init__()
        self.system_prompt = &quot;&quot;&quot;
        You are a professional ESG data analysis agent. Your core mission is to query the ESG data of a particular company requested by the user in the database, and based on this, provide ESG analysis and recommendations.

        Perform the following tasks sequentially:

        1. Data Inquiry and Validation:
        - Inquire the ESG data of the company in the database based on user-provided ticker symbols
        - Check data up-to-date and check missing information
        - Evaluate quality and scope of searched data

        2. ESG Comprehensive Analysis:
        - Analysis of scores and key indicators for each area of the environment (E), society (S), and governance (G)
        - Identify the relative position of the company relative to the ESG average in the industry
        - Identify ESG rating fluctuations and key variables
        - Investigate key ESG issues and controversies

        3. Sustainability Assessment:
        - Analysis of ESG policies, objectives, and practices of the enterprise
        - Evaluate key ESG factors such as climate change response, resource efficiency, human capital management, and board composition
        - Analysis of long-term ESG risks and opportunities

        4. Investment Opinion:
        - Objective investment recommendation based solely on ESG data (buy/hold/sell)
        - Identify strengths, weaknesses, opportunities, and threats from an ESG perspective
        - Potential valuation from a sustainable investment perspective

        5. Create a report:
        - Executive Summary
        - Basic information such as ticker, company name, industry group, date of analysis, etc
        - ESG-based investment strategies and recommendations

        All analyses and responses should be written in Korean, and provide accurate and objective information that will substantially assist investors in making ESG-oriented decisions. Do not include financial data or financial analyses, but only present assessments and recommendations purely from the ESG data and sustainability perspective.
        If you fail to get the data, answer that you can't find the data.
        &quot;&quot;&quot;
        self.agent = None
        self.tools = [ESGDataTool()]

    def _run(self, state: dict) -&amp;gt; dict:
        if self.agent is None:
            assert state[&quot;llm&quot;] is not None, &quot;The State model should include llm&quot;
            llm = state[&quot;llm&quot;]
            self.agent = create_react_agent(
                llm,
                self.tools,
                prompt=self.system_prompt,
            )
        result = self.agent.invoke(state)
        self.logger.info(f&quot;   result: \n{result['messages'][-1].content}&quot;)
        return Command(
            update={
                &quot;messages&quot;: [
                    HumanMessage(
                        content=result[&quot;messages&quot;][-1].content,
                        name=self.__class__.__name__.lower().replace(&quot;node&quot;, &quot;&quot;),
                    )
                ]
            },
            goto=&quot;supervisor&quot;,
        )

    def _invoke(self, query: str) -&amp;gt; RawResponse:
        agent = self.agent or create_react_agent(
            ChatOpenAI(model=self.DEFAULT_LLM_MODEL),
            self.tools,
            prompt=self.system_prompt,
        )
        result = agent.invoke({&quot;messages&quot;: [(&quot;human&quot;, query)]})
        return RawResponse(answer=result[&quot;messages&quot;][-1].content)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4. 구현 시 고려사항&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) 동기/비동기 처리&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;API 호출을 위한 동기/비동기 메서드 모두 구현&lt;/li&gt;
&lt;li&gt;대부분의 경우 동기 메서드를 사용하며, 필요시 비동기 메서드 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) 오류 처리&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;API 호출 실패, 데이터 없음 등 다양한 예외 상황 처리&lt;/li&gt;
&lt;li&gt;사용자 친화적인 오류 메시지 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3) 확장성&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새로운 ESG 데이터 소스 추가가 용이하도록 설계&lt;/li&gt;
&lt;li&gt;다양한 Tool을 추가하여 기능 확장 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;5. 실제 사용 예시&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# Agent 초기화
esg_node = RetrieveESGNode()

# ESG 데이터 조회 요청
response = esg_node._invoke(&quot;테슬라의 ESG 데이터를 분석해주세요&quot;)
print(response.answer)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 실행하면 Agent는 하단과 같은 결과를 출력해주는 모습입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1420&quot; data-origin-height=&quot;664&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lgfli/btsNtSNQgTo/kwLJnDwDgSqGg9qh0uWQR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lgfli/btsNtSNQgTo/kwLJnDwDgSqGg9qh0uWQR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lgfli/btsNtSNQgTo/kwLJnDwDgSqGg9qh0uWQR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flgfli%2FbtsNtSNQgTo%2FkwLJnDwDgSqGg9qh0uWQR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1420&quot; height=&quot;664&quot; data-origin-width=&quot;1420&quot; data-origin-height=&quot;664&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;느낀점&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangChain의 Tool calling 기능을 활용하면 복잡한 ESG 데이터 조회 및 분석 작업을 자연어 인터페이스로 간단하게 구현할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본 구현에서는 yfinance API를 기본 데이터 소스로 사용했지만, 추가적인 ESG 데이터 제공업체의 API를 통합하거나 자체 데이터베이스를 연동하는 방식으로 확장할 수 있습니다. 또한, 다른 금융 데이터를 조회하는 Tool들을 추가하여 종합적인 투자 분석 시스템으로 발전시킬 수 있습니다.&lt;/p&gt;</description>
      <category>Activities/FinAgent Lab</category>
      <category>ESG</category>
      <category>LangChain</category>
      <category>tool calling</category>
      <category>yfinace api</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/99</guid>
      <comments>https://striver.tistory.com/entry/Agent-Fuction-calling%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-Agent#entry99comment</comments>
      <pubDate>Tue, 22 Apr 2025 22:23:53 +0900</pubDate>
    </item>
    <item>
      <title>[PR] 실전 Fork &amp;amp; PR</title>
      <link>https://striver.tistory.com/entry/XFile</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;전에 Fork와 PR을 간단하게 코드로 설명하면서 진행했던 글이 있는데, 이번에는 조금 더 심화적으로 협업중 발생한 문제를 해결하면서 PR을 진행하는 과정에 대해 설명하도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 장기간 Merge &amp;amp; PR을 하지않아, 원본 레파지토리의 히스토리가 쌓이고 Fork한 원격 레파지토리에서 새롭게 브랜치를 생성해서 커밋이 쌓인 상황입니다. 따라서 히스토리 충돌이 발생하지 않도록, 원본 레파지토리의 코드를 잘 가져와서 병합 후 PR을 요청하는 과정을 다뤄보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원본 레파지토리의 최신 코드를 가져오기 위해 &quot;git fetch upstream&quot; 명령어를 실행했는데 아래의 에러가 발생하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;468&quot; data-origin-height=&quot;105&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lM2tQ/btsNvCYgrmJ/gqaoW67aMRKvY2QXwgPLyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lM2tQ/btsNvCYgrmJ/gqaoW67aMRKvY2QXwgPLyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lM2tQ/btsNvCYgrmJ/gqaoW67aMRKvY2QXwgPLyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlM2tQ%2FbtsNvCYgrmJ%2FgqaoW67aMRKvY2QXwgPLyk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;468&quot; height=&quot;105&quot; data-origin-width=&quot;468&quot; data-origin-height=&quot;105&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인해보니 upstream이라는 이름의 원격 저장소가 등록이 되지 않아 발생한 에러였습니다. &quot;git remote -v&quot; 로 확인 가능합니다. 원본 레포지토리를 upstream이라는 이름으로 추가해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745391918043&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git remote add upstream https://github.com/원본유저/원본레포.git&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 다시 git fetch upstream으로 원본 레파지토리의 변경사항을 가져와줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;206&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKfsHX/btsNwMNuGmF/fIRUrZpDfIckU2o8EBt4kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKfsHX/btsNwMNuGmF/fIRUrZpDfIckU2o8EBt4kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKfsHX/btsNwMNuGmF/fIRUrZpDfIckU2o8EBt4kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKfsHX%2FbtsNwMNuGmF%2FfIRUrZpDfIckU2o8EBt4kk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;632&quot; height=&quot;206&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;206&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러가 발생하지않고 명령어가 잘 작동한 모습인데, 로컬 코드와 히스토리가 변경된게 없어서 뭔가 잘못됐나?라는 생각을 했습니다. 찾아보니 원본 변경사항을 로컬 브랜치에 병합하는 추가 과정이 필요한 것이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745392149475&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git checkout dev
git merge upstream/dev&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 내가 병합하고자 하는 브랜치로 이동한 후 원격 저장소에서 가져온 브랜치의 코드를 merge 해주면 되는 것 이었습니다. 이로써 1차적으로 원본 저장소의 코드를 로컬 저장소로 병합하였습니다. 이제 제 작업 브랜치로 이동해서 이 병합한 코드를 다시 머지해서 코드에 문제가 없는지 검증을 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745392365339&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git checkout feat/retrieve_docs
git merge dev&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령어들을 통해 제 작업 브랜치와 원본 저장소에서 가져오는 코드들이 병합 되다보니 당연히 충돌이 발생했고, 이 부분들을 수동으로 해결해주었습니다. 그리고 다시 실행하려는데 병합중 가상환경에 다운로드 된 패키지 항목이 달라지면서 Import 에러가 발생해 uv에 새롭게 패키지를 추가해주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745392721485&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uv pip install 패키지명&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 만약 uv 가상환경을 pyproject.toml 같은 의존성 파일로 관리하고 있을 경우, uv sync 의 명렁어 사용시 uv pip install로 설치한 패키지는 사라지기 때문에 필요에 따라 의존성 파일 자체에 패키지명을 기입하는 것도 좋은 방법입니다. 병합 후 파일을 실행해 오류없이 작동하는 것을 확인하면, dev 브랜치로 다시 이동해 기존 작업 브랜치의 코드를 다시 병합해 PR전 마지막으로 검토를 진행해줍니다. 이상이 없음이 확인되면 로컬 저장소의 코드를 원격 저장소의 코드로 Push 해줍니다. 그리고 깃허브 페이지로 가보면 저장소 상단에 PR 창이 뜨는데 안뜨길래 뭐가 문젠가 싶어서 직접 PR 생성 창으로 이동해보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1291&quot; data-origin-height=&quot;519&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0Wyhc/btsNxqo7Lrb/hAsh2Bmhf3Ly1AbafB9UkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0Wyhc/btsNxqo7Lrb/hAsh2Bmhf3Ly1AbafB9UkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0Wyhc/btsNxqo7Lrb/hAsh2Bmhf3Ly1AbafB9UkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0Wyhc%2FbtsNxqo7Lrb%2FhAsh2Bmhf3Ly1AbafB9UkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1291&quot; height=&quot;519&quot; data-origin-width=&quot;1291&quot; data-origin-height=&quot;519&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;base와 compare이 동일한 dev 브랜치를 비교하고 있기 때문에 PR을 생성해도 변경사항이 없을 것으로 판단해 PR이 불가능한것으로 보였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1317&quot; data-origin-height=&quot;550&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CU7R4/btsNtRbdPDC/eOeMJRfqjucYY5pmkAmh60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CU7R4/btsNtRbdPDC/eOeMJRfqjucYY5pmkAmh60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CU7R4/btsNtRbdPDC/eOeMJRfqjucYY5pmkAmh60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCU7R4%2FbtsNtRbdPDC%2FeOeMJRfqjucYY5pmkAmh60%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1317&quot; height=&quot;550&quot; data-origin-width=&quot;1317&quot; data-origin-height=&quot;550&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 compare across forks를 클릭해 fork를 한 원본 저장소의 브랜치를 지정하려했는데 이 부분이 잘 안됐습니다. 지정하고 싶은 브랜치명이 안나오길래 원인을 찾고 있었는데 원본 레파지토리의 Fork 목록에 카운팅이 사라진것을 확인했습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1263&quot; data-origin-height=&quot;59&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zAkgd/btsNwrpuNKl/gQxaQsPc7nKOL9vIfMCQd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zAkgd/btsNwrpuNKl/gQxaQsPc7nKOL9vIfMCQd1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zAkgd/btsNwrpuNKl/gQxaQsPc7nKOL9vIfMCQd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzAkgd%2FbtsNwrpuNKl%2FgQxaQsPc7nKOL9vIfMCQd1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1263&quot; height=&quot;59&quot; data-origin-width=&quot;1263&quot; data-origin-height=&quot;59&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Fork 연결이 끊겼나? 이런 생각이 들어 기존 코드들을 백덥해두고 레파지토리를 새롭게 만들어보았습니다. 기존 레파지토리를 사용해보려했는데 원본 저장소가 private -&amp;gt; public 으로 바뀌면서 혹시나 모를 key 유출을 대비해 새롭게 만들기로 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1274&quot; data-origin-height=&quot;499&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bebANv/btsNwMfSlYd/3DbxlpKffHde5IECgFR5I0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bebANv/btsNwMfSlYd/3DbxlpKffHde5IECgFR5I0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bebANv/btsNwMfSlYd/3DbxlpKffHde5IECgFR5I0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbebANv%2FbtsNwMfSlYd%2F3DbxlpKffHde5IECgFR5I0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1274&quot; height=&quot;499&quot; data-origin-width=&quot;1274&quot; data-origin-height=&quot;499&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Push 후 PR 창을 들어가보니 위와같이 PR을 할 수 있는 이벤트가 생겼길래 이를 활용해 PR을 날려주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;929&quot; data-origin-height=&quot;696&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkxXYC/btsNw29ZEH0/MwLLLdjeSfiJEk49ApMno1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkxXYC/btsNw29ZEH0/MwLLLdjeSfiJEk49ApMno1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkxXYC/btsNw29ZEH0/MwLLLdjeSfiJEk49ApMno1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkxXYC%2FbtsNw29ZEH0%2FMwLLLdjeSfiJEk49ApMno1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;929&quot; height=&quot;696&quot; data-origin-width=&quot;929&quot; data-origin-height=&quot;696&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Dev/Git&amp;amp;GitHub</category>
      <category>fork</category>
      <category>git</category>
      <category>github</category>
      <category>PR</category>
      <category>협업</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/98</guid>
      <comments>https://striver.tistory.com/entry/XFile#entry98comment</comments>
      <pubDate>Mon, 21 Apr 2025 22:19:38 +0900</pubDate>
    </item>
    <item>
      <title>[LLM 평가]LLM 및 RAG 평가 프레임워크 비교 분석</title>
      <link>https://striver.tistory.com/entry/EvaluationLLM-%EB%B0%8F-RAG-%ED%8F%89%EA%B0%80-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-%EB%B9%84%EA%B5%90-%EB%B6%84%EC%84%9D</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;LLM(Large Language Model)과 RAG(Retrieval-Augmented Generation) 시스템의 성능을 평가하는 것은 AI 애플리케이션 개발에서 중요한 과정입니다. 다양한 평가 프레임워크들이 각자 독특한 특징과 접근 방식을 제공합니다. 이 글에서는 주요 평가 프레임워크들을 비교하고 각각의 장단점을 살펴보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. LLM-as-a-judge&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) 개념&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LLM이 평가자 역할을 수행하여 다른 모델이나 시스템의 성능을 평가&lt;/li&gt;
&lt;li&gt;인간 평가자 대신 LLM을 활용하여 대규모 평가 가능&lt;/li&gt;
&lt;li&gt;프롬프트 엔지니어링을 통해 평가 기준과 방법 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) 장점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인간 평가보다 비용 효율적&lt;/li&gt;
&lt;li&gt;일관된 평가 기준 적용 가능&lt;/li&gt;
&lt;li&gt;대규모 평가에 적합&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3) 한계&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LLM 자체의 편향이 평가에 영향을 줄 수 있음&lt;/li&gt;
&lt;li&gt;특정 언어나 도메인에 따라 성능 차이 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 주요 평가 프레임워크&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) RAGAS&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특징:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가장 널리 사용되는 RAG 평가 프레임워크&lt;/li&gt;
&lt;li&gt;LLM-as-a-judge 기반으로 작동&lt;/li&gt;
&lt;li&gt;Retrieval 정답 데이터(ground truth) 없이도 평가 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;장점:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;에이전트의 tool 사용 평가 지원&lt;/li&gt;
&lt;li&gt;SQL 메트릭 및 전통적 NLP 스코어 포함&lt;/li&gt;
&lt;li&gt;검색 품질 평가에 강점&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;단점:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 단락에 대한 연관성 비교로 비용 부담&lt;/li&gt;
&lt;li&gt;다국어 지원 부족 (한국어 평가 시 성능 저하 가능)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) DeepEval&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특징:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RAGAS, G-Eval 등 LLM-as-a-judge 메트릭 포함&lt;/li&gt;
&lt;li&gt;AI Safety 관련 평가 메트릭 제공&lt;/li&gt;
&lt;li&gt;CI/CD 통합 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;장점:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다양한 벤치마크 데이터셋 지원&lt;/li&gt;
&lt;li&gt;LLM 평가에 특화된 기능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;단점:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다국어 지원 부족&lt;/li&gt;
&lt;li&gt;Retrieval 메트릭 부재 (RAG보다 LLM 평가 중심)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3) OpenAI Evals&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특징:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OpenAI에서 직접 개발한 평가 프레임워크&lt;/li&gt;
&lt;li&gt;OpenAI 대시보드에서 바로 사용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;장점:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;간편한 사용법&lt;/li&gt;
&lt;li&gt;LLM 성능 평가에 최적화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;단점:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OpenAI 모델만 사용 가능&lt;/li&gt;
&lt;li&gt;제한된 커스터마이징 옵션&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;4) Langsmith&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특징:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Langchain과 원활한 연동&lt;/li&gt;
&lt;li&gt;모니터링과 테스트 기능 통합&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;장점:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;직관적인 대시보드로 평가 결과 시각화&lt;/li&gt;
&lt;li&gt;LLM-as-a-judge 및 커스텀 메트릭 지원&lt;/li&gt;
&lt;li&gt;개발에서 프로덕션까지 일관된 환경&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;단점:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Langchain 생태계에 의존적&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;5) Trulens&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특징:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Human-in-the-loop 평가에 특화&lt;/li&gt;
&lt;li&gt;AI Safety 중심 메트릭&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;장점:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;결과 확인을 위한 대시보드 제공&lt;/li&gt;
&lt;li&gt;반복적 평가 프로세스 지원&lt;/li&gt;
&lt;li&gt;관찰 가능성(Observability) 강화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;단점:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;학습 곡선이 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;6) Huggingface Evaluate&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특징:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다양한 NLP 메트릭과 데이터셋 제공&lt;/li&gt;
&lt;li&gt;간편한 사용법&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;장점:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전통적 NLP 평가 메트릭 쉽게 활용 가능&lt;/li&gt;
&lt;li&gt;빠른 구현과 통합&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;단점:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외부 라이브러리 의존성 높음&lt;/li&gt;
&lt;li&gt;LLM 특화 평가보다는 일반적 NLP 평가에 중점&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;7) ARES&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특징:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스탠포드 대학에서 개발&lt;/li&gt;
&lt;li&gt;평가용 소형 LLM(sLLM) 파인튜닝 접근법&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;장점:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LLM-as-a-judge 중 높은 정확도&lt;/li&gt;
&lt;li&gt;Natural Question 데이터셋 기반 평가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;단점:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;높은 연산 요구사항&lt;/li&gt;
&lt;li&gt;복잡한 설정 과정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;8) AutoRAG&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특징:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RAG에 특화된 최적화 프레임워크&lt;/li&gt;
&lt;li&gt;다양한 메트릭 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;장점:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Retrieval 평가 및 최적화 특화&lt;/li&gt;
&lt;li&gt;한국어, 영어, 일본어 데이터셋 생성 지원&lt;/li&gt;
&lt;li&gt;평가 대시보드 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;단점:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상대적으로 새로운 프레임워크로 커뮤니티 지원이 적을 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 프레임워크 선택 가이드&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) RAG 시스템 평가&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RAGAS나 AutoRAG가 적합&lt;/li&gt;
&lt;li&gt;검색 품질과 생성 품질 모두 평가 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) LLM 자체 평가&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DeepEval, OpenAI Evals, ARES 고려&lt;/li&gt;
&lt;li&gt;응답 품질, 정확성, 안전성 등 다양한 측면 평가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3) 통합 모니터링 및 평가&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Langsmith나 Trulens 활용&lt;/li&gt;
&lt;li&gt;프로덕션 환경에서의 지속적 모니터링에 적합&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM과 RAG 평가 프레임워크는 각각 독특한 강점을 가지고 있습니다. 프로젝트의 요구사항, 평가하려는 시스템의 유형, 그리고 필요한 메트릭에 따라 적절한 프레임워크를 선택하는 것이 중요합니다. 대부분의 경우, 하나의 프레임워크만으로는 모든 평가 요구사항을 충족하기 어려울 수 있으므로, 여러 프레임워크를 조합하여 사용하는 것도 효과적인 전략입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Fastcampus - &lt;/span&gt;RAG 평가와 개선의 모든 것 : 데이터셋 제작부터 agent 평가까지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>LLM Engineering/LLMops</category>
      <category>autorag</category>
      <category>LLM</category>
      <category>LLM Evaluation</category>
      <category>llm 평가</category>
      <category>Rag</category>
      <category>ragas</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/97</guid>
      <comments>https://striver.tistory.com/entry/EvaluationLLM-%EB%B0%8F-RAG-%ED%8F%89%EA%B0%80-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-%EB%B9%84%EA%B5%90-%EB%B6%84%EC%84%9D#entry97comment</comments>
      <pubDate>Fri, 18 Apr 2025 21:33:05 +0900</pubDate>
    </item>
    <item>
      <title>[RAG Evaluation] RAG 평가의 기초</title>
      <link>https://striver.tistory.com/entry/RAG-Evaluation-RAG-%ED%8F%89%EA%B0%80%EC%9D%98-%EA%B8%B0%EC%B4%88</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;RAG(Retrieval Augmented Generation) 시스템은 정보 검색(Retrieval)과 텍스트 생성(Generation)이라는 두 가지 핵심 요소로 구성됩니다. 이 시스템의 성능을 효과적으로 평가하기 위해서는 각 요소를 독립적으로, 그리고 전체 시스템을 통합적으로 평가하는 것이 중요합니다. 이 글에서는 RAG 시스템 평가에 사용되는 다양한 방법론과 메트릭에 대해 살펴보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. RAG 평가의 기본 개념&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAG 시스템은 일반적으로 Retrieval(검색)과 Generation(생성) 부분으로 나누어 평가합니다. 하지만 Retrieval의 성능이 Generation의 성능에 직접적인 영향을 미치기 때문에, 두 요소를 완벽하게 분리해서 평가하는 것은 불가능합니다. 이러한 복잡성을 고려하여 RAG 평가는 크게 두 가지 접근 방식으로 진행됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정성적 평가: 주관적 판단에 기반한 평가&lt;/li&gt;
&lt;li&gt;정량적 평가: 수치화된 메트릭을 통한 객관적 평가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 두 방식을 적절히 조합하여 RAG 시스템의 성능을 종합적으로 판단할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 정성적 평가 방법&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정성적 평가는 연구자나 도메인 전문가의 주관적 판단에 의해 이루어지는 평가 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 장점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도메인 전문가의 지식과 경험을 바탕으로 한 심층적인 평가 가능&lt;/li&gt;
&lt;li&gt;맥락과 의미를 고려한 질적 평가 가능&lt;/li&gt;
&lt;li&gt;수치화하기 어려운 미묘한 차이나 뉘앙스 포착 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 단점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;평가자의 주관에 따라 결과가 달라질 수 있음&lt;/li&gt;
&lt;li&gt;대규모 평가에 시간과 비용이 많이 소요됨&lt;/li&gt;
&lt;li&gt;재현성이 낮을 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 적용 분야&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Retrieval: Langsmith와 같은 추적 도구를 사용하거나 답변 생성 과정 출력을 통해 검색된 컨텍스트를 직접 확인&lt;/li&gt;
&lt;li&gt;Generation: 최종 답변의 적합성, 허위 정보 포함 여부, 컨텍스트와의 일치성 등을 판단&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정성적 평가는 특히 도메인 전문가의 판단이 중요한 분야에서 정량적 평가보다 더 신뢰할 만한 결과를 제공할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 정량적 평가 방법&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정량적 평가는 객관적인 수치를 통해 시스템의 성능을 측정하는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 장점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;판단을 수치화하여 객관적인 비교 가능&lt;/li&gt;
&lt;li&gt;평가 데이터셋 구축이 선행되어야 함&lt;/li&gt;
&lt;li&gt;재현성이 높고 대규모 평가에 적합&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 고려사항&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;평가 데이터셋의 품질과 다양성이 결과에 큰 영향을 미침&lt;/li&gt;
&lt;li&gt;데이터셋 구축 시 &quot;어느 정도의 양이 필요한가&quot;에 대한 고민 필요&lt;/li&gt;
&lt;li&gt;정성적 평가와 정량적 평가를 융합한 새로운 메트릭 개발 가능성 존재&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 적용 분야&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Retrieval: Context Precision, Context Recall 등의 메트릭으로 검색 성능 평가&lt;/li&gt;
&lt;li&gt;Generation: Faithfulness, Answer Relevancy 등의 지표로 생성 품질 평가&lt;/li&gt;
&lt;li&gt;Total Process: Answer Correctness, Answer Semantic Similarity 등으로 전체 파이프라인 평가&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4. Ragas 평가 메트릭 상세 분석&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ragas는 RAG 시스템 평가를 위한 주요 메트릭을 제공하는 프레임워크입니다. 각 메트릭은 0~1 사이의 값을 가지며, 높을수록 더 좋은 성능을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) Generation 평가 메트릭&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Faithfulness (충실도)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적: 생성된 답변이 얼마나 제공된 컨텍스트에 충실한지 평가&lt;/li&gt;
&lt;li&gt;의미: 답변이 컨텍스트에 기반하여 생성되었는지, 아니면 LLM의 사전 지식에 의존했는지 판단&lt;/li&gt;
&lt;li&gt;중요성: 컨텍스트가 잘못되었는데도 LLM이 자체 지식으로 올바른 답변을 생성하는 경우도 hallucination으로 간주될 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Answer Relevancy (답변 관련성)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적: 생성된 답변이 주어진 질문에 적절히 대응하는지 평가&lt;/li&gt;
&lt;li&gt;평가 방식: 생성된 답변을 기반으로 LLM을 통해 역으로 질문을 생성한 후, 이 질문과 실제 질문 간의 코사인 유사도 계산&lt;/li&gt;
&lt;li&gt;중요성: 답변이 질문의 의도를 정확히 파악했는지 확인 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) Retrieval 평가 메트릭&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context Precision (컨텍스트 정밀도)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적: 검색된 컨텍스트 중 질문과 관련된 정보의 비율 측정&lt;/li&gt;
&lt;li&gt;고려사항: 검색할 문서 개수에 따라 점수가 달라질 수 있음&lt;/li&gt;
&lt;li&gt;중요성: 검색된 정보의 품질과 관련성 평가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context Recall (컨텍스트 재현율)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적: 검색된 컨텍스트가 필요한 정보를 얼마나 포함하는지 평가&lt;/li&gt;
&lt;li&gt;평가 방식: Ground truth에 포함된 statement 중 검색된 컨텍스트에 포함된 비율 계산&lt;/li&gt;
&lt;li&gt;중요성: 답변 생성에 필요한 정보가 누락 없이 포함되었는지 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context Relevancy (컨텍스트 적합성)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적: 컨텍스트에 관련 없는 내용이 얼마나 포함되었는지 평가&lt;/li&gt;
&lt;li&gt;계산 방식: 전체 추출한 컨텍스트 문장 수 중 적합 문장의 비율 계산&lt;/li&gt;
&lt;li&gt;고려사항: 컨텍스트 길이에 따라 값이 달라질 수 있음 (길이가 길수록 오류 정보도 많이 포함될 가능성)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) End-to-End 평가 메트릭&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Answer Semantic Similarity (답변 의미적 유사도)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적: 생성된 답변과 Ground Truth 간의 의미적 유사성 평가&lt;/li&gt;
&lt;li&gt;계산 방식: 벡터 임베딩을 통한 유사도 평균 계산&lt;/li&gt;
&lt;li&gt;의미: 답변이 정답과 얼마나 의미적으로 가까운지 측정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Answer Correctness (답변 정확도)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적: 생성된 답변의 사실적 정확성 평가&lt;/li&gt;
&lt;li&gt;특징: 단순한 문장 유사성이 아닌 사실적 내용의 정확성 평가&lt;/li&gt;
&lt;li&gt;의미: Semantic Similarity와 달리 내용의 정확성에 중점&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;5. RAG 평가의 실제 적용&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAG 시스템 평가를 실제로 적용할 때 고려해야 할 점들입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 통합적 평가의 중요성&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Retrieval과 Generation 각각의 평가뿐만 아니라 전체 시스템의 성능 평가 필요&lt;/li&gt;
&lt;li&gt;개별 구성 요소의 성능이 좋아도 전체 시스템의 성능이 반드시 우수하다고 볼 수 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;2) 도메인 특성 고려&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 도메인에 따라 중요시되는 메트릭이 다를 수 있음&lt;/li&gt;
&lt;li&gt;의료, 법률 등 정확성이 중요한 분야에서는 Faithfulness와 Answer Correctness가 더 중요할 수 있음&lt;/li&gt;
&lt;li&gt;일반적인 질의응답에서는 Context Relevancy와 Answer Relevancy가 더 중요할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 평가 데이터셋 구축&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다양한 질문 유형과 난이도를 포함한 균형 잡힌 데이터셋 필요&lt;/li&gt;
&lt;li&gt;도메인 전문가의 검증을 거친 Ground Truth 구축&lt;/li&gt;
&lt;li&gt;정기적인 데이터셋 업데이트로 시스템 발전 추적&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) 정성적 평가와 정량적 평가의 균형&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;두 평가 방식의 장단점을 상호 보완하는 평가 체계 구축&lt;/li&gt;
&lt;li&gt;정량적 메트릭으로 대규모 평가 후, 중요 사례에 대해 정성적 평가 진행&lt;/li&gt;
&lt;li&gt;사용자 피드백과 전문가 평가를 함께 고려&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAG 시스템의 평가는 여러가지 벤치마크 데이터셋이 존재하고 다양한 기법이 존재합니다. 그 중 상황에 맞게 알맞은 방법을 선택하고 평가를 진행할 수 있도록 많은 공부가 필요하겠다는 생각이 들었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://velog.io/@cathx618/RAG-%ED%8F%89%EA%B0%80-%EB%B0%A9%EB%B2%95-%EC%A0%95%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@cathx618/RAG-%ED%8F%89%EA%B0%80-%EB%B0%A9%EB%B2%95-%EC%A0%95%EB%A6%AC&lt;/a&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>LLM Engineering/LLMops</category>
      <category>rag evaluation</category>
      <category>rag 평가</category>
      <category>정량적 평가</category>
      <category>정성적 평가</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/96</guid>
      <comments>https://striver.tistory.com/entry/RAG-Evaluation-RAG-%ED%8F%89%EA%B0%80%EC%9D%98-%EA%B8%B0%EC%B4%88#entry96comment</comments>
      <pubDate>Thu, 17 Apr 2025 21:45:47 +0900</pubDate>
    </item>
    <item>
      <title>[DeepLearning] 딥러닝의 기초와 메모리 최적화 기법 복습</title>
      <link>https://striver.tistory.com/entry/%EB%B3%B5%EC%8A%B5-%EB%94%A5%EB%9F%AC%EB%8B%9D%EC%9D%98-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC%EC%99%80-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%B5%9C%EC%A0%81%ED%99%94-%EA%B8%B0%EB%B2%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 복습겸 딥러닝의 기초과 되는 핵심 원리와 기법에 대해 간단하게 얘기하고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 딥러닝의 학습 과정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) 순전파&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력 데이터가 모델의 각 층을 거쳐 예측값을 생성합니다. 이 과정에 각 층의 계산 결과와 상태값이 메모리에 저장됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) 손실 계산&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델의 예측값과 실제 정답 간의 차이 즉, 손실을 계산합니다. 이 손실은 모델의 정확도를 측정하는 지표가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3) 역전파&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계산된 손실을 기준으로 각 층을 거슬러 올라가며 가중치가 손실에 미치는 영향 즉, 그래디언트를 계산합니다. 이때 순전파에서 저장해둔 상태값들이 활용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;4) 가중치 업데이트&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계산된 그래디언트를 바탕으로 옵티마이저가 모델의 가중치를 조정합니다. 이 과정을 통해 모델은 점점 더 정확한 예측을 할 수 있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 딥러닝의 핵심 구성 요소&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) 레이어(Layer)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레이어 또는 층은 인공 신경망의 기본 구조 단위입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력 데이터를 받아 특정 연산을 수행&lt;/li&gt;
&lt;li&gt;연산 결과를 다음 레이어로 전달&lt;/li&gt;
&lt;li&gt;가중치와 활성화 함수를 포함하여 데이터의 특징을 추출하거나 변환&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) 활성화 함수(Activation Function)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;신경망에 비선형성을 도입하여 복잡한 패턴 학습 가능&lt;/li&gt;
&lt;li&gt;ReLU, Sigmoid, Tanh, LeakyReLU 등 다양한 종류 존재&lt;/li&gt;
&lt;li&gt;각 활성화 함수는 고유한 특성과 적합한 사용 사례 보유&lt;/li&gt;
&lt;li&gt;기울기 소실 문제 해결을 위한 특수 설계된 활성화 함수들 발전&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3) 손실 함수(Loss Function)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델의 예측과 실제 정답 간의 차이를 수치화 및 학습 목표 정의&lt;/li&gt;
&lt;li&gt;회귀 문제: MSE(Mean Squared Error), MAE(Mean Absolute Error)&lt;/li&gt;
&lt;li&gt;분류 문제: Cross-Entropy Loss, BCE(Binary Cross-Entropy)&lt;/li&gt;
&lt;li&gt;특수 목적: Triplet Loss(유사도 학습), Focal Loss(클래스 불균형)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;4) 옵티마이저(Optimizer)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그래디언트를 기반으로 모델 파라미터를 업데이트하는 알고리즘&lt;/li&gt;
&lt;li&gt;SGD, Adam, RMSprop, AdamW 등 다양한 알고리즘 존재&lt;/li&gt;
&lt;li&gt;학습률 스케줄링을 통한 최적화 성능 향상&lt;/li&gt;
&lt;li&gt;모멘텀, 적응적 학습률 등의 기법 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;5) 그래디언트&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래디언트는 손실 함수의 기울기를 의미합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 가중치 파라미터가 손실에 미치는 영향을 수치화한 값으로 손실 함수를 파라미터에 대해 미분하여 계산&lt;/li&gt;
&lt;li&gt;그래디언트의 방향은 손실이 가장 빠르게 증가하는 방향&lt;/li&gt;
&lt;li&gt;옵티마이저는 그래디언트의 반대 방향으로 파라미터를 업데이트&lt;/li&gt;
&lt;li&gt;학습률(learning rate)은 파라미터 업데이트 크기를 결정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;6) 정규화 기법(Regularization)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Dropout: 학습 중 일부 뉴런을 임의로 비활성화&lt;/li&gt;
&lt;li&gt;가중치 감소(Weight Decay): L1/L2 정규화를 통한 모델 복잡도 제한&lt;/li&gt;
&lt;li&gt;배치 정규화(Batch Normalization): 내부 공변량 이동 감소&lt;/li&gt;
&lt;li&gt;데이터 증강(Data Augmentation): 학습 데이터 다양화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 대규모 모델 훈련을 위한 메모리 최적화 기법&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딥러닝 모델이 커질수록 메모리 요구량도 증가합니다. 제한된 리소스에서 효율적인 훈련을 위한 주요 기법 몇가지를 정리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) KV 캐시(Key-Value Cache)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;개념&lt;/b&gt;: 이전에 계산한 키(Key)와 값(Value) 결과를 메모리에 저장해 재활용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;효과&lt;/b&gt;: 동일한 입력 토큰에 대한 중복 계산 방지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;적용&lt;/b&gt;: 주로 트랜스포머 기반 모델(GPT, BERT 등)의 어텐션 메커니즘에서 사용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이점&lt;/b&gt;: 추론 속도 향상 및 계산 비용 절감&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) 그래디언트 체크포인팅(Gradient Checkpointing)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;개념&lt;/b&gt;: 모든 중간 활성화값 대신 일부 체크포인트만 저장&lt;/li&gt;
&lt;li&gt;&lt;b&gt;작동방식&lt;/b&gt;: 역전파 시 필요한 중간값을 체크포인트로부터 재계산&lt;/li&gt;
&lt;li&gt;&lt;b&gt;트레이드오프&lt;/b&gt;: 메모리 사용량 감소 &amp;harr; 계산 시간 증가&lt;/li&gt;
&lt;li&gt;&lt;b&gt;적용 사례&lt;/b&gt;: 초대형 언어 모델, 고해상도 이미지 처리 모델&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3) 기타 메모리 최적화 기법&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;양자화(Quantization)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;32비트 부동소수점 &amp;rarr; 16비트, 8비트, 4비트로 정밀도 축소&lt;/li&gt;
&lt;li&gt;모델 크기 및 메모리 사용량 대폭 감소&lt;/li&gt;
&lt;li&gt;약간의 성능 손실이 발생할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;모델 병렬화(Model Parallelism)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대규모 모델을 여러 GPU/TPU에 분산 배치&lt;/li&gt;
&lt;li&gt;파이프라인 병렬화: 모델 레이어를 여러 장치에 분할&lt;/li&gt;
&lt;li&gt;텐서 병렬화: 단일 연산을 여러 장치에서 병렬 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;혼합 정밀도 훈련(Mixed Precision Training)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FP32와 FP16을 혼합하여 사용&lt;/li&gt;
&lt;li&gt;메모리 사용량 감소 및 계산 속도 향상&lt;/li&gt;
&lt;li&gt;스케일링 기법을 통해 정확도 손실 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/DeepLearning</category>
      <category>가중치 업데이트</category>
      <category>딥러닝 학습</category>
      <category>메모리 최적화</category>
      <category>순전파</category>
      <category>양자화</category>
      <category>역전파</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/95</guid>
      <comments>https://striver.tistory.com/entry/%EB%B3%B5%EC%8A%B5-%EB%94%A5%EB%9F%AC%EB%8B%9D%EC%9D%98-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC%EC%99%80-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%B5%9C%EC%A0%81%ED%99%94-%EA%B8%B0%EB%B2%95#entry95comment</comments>
      <pubDate>Wed, 16 Apr 2025 22:12:52 +0900</pubDate>
    </item>
    <item>
      <title>[Transformer] 인코더와 디코더</title>
      <link>https://striver.tistory.com/entry/Transformer-%EC%9D%B8%EC%BD%94%EB%8D%94%EC%99%80-%EB%94%94%EC%BD%94%EB%8D%94</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 인코더&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인코더는 입력 텍스트를 이해하고 의미를 추출하는 역할을 합니다. 기본적으로 여러 개의 동일한 층이 반복되는 구조로 되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1065&quot; data-origin-height=&quot;1318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9AlE9/btsNlLbdS3N/H9otqKgILAdv56LKnmKfKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9AlE9/btsNlLbdS3N/H9otqKgILAdv56LKnmKfKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9AlE9/btsNlLbdS3N/H9otqKgILAdv56LKnmKfKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9AlE9%2FbtsNlLbdS3N%2FH9otqKgILAdv56LKnmKfKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;499&quot; height=&quot;618&quot; data-origin-width=&quot;1065&quot; data-origin-height=&quot;1318&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) 인코더의 주요 구성 요소&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;층 정규화(Layer Normalization)&lt;/b&gt;: 데이터의 분포를 조정하여 학습을 안정화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;멀티 헤드 어텐션(Multi-Head Attention)&lt;/b&gt;: 입력 시퀀스의 다양한 관계를 파악&lt;/li&gt;
&lt;li&gt;&lt;b&gt;피드 포워드 층(Feed Forward Layer)&lt;/b&gt;: 비선형 변환을 통해 표현력 강화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) 잔차 연결&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잔차 연결은 이미지에 보이는 것처럼 원래 입력값을 각 하위 층의 출력에 더해주는 방식을 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;그래디언트 소실 문제 해결&lt;/b&gt;: 깊은 네트워크에서 학습 신호가 손실되는 문제 방지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;학습 안정화&lt;/b&gt;: 층이 많아져도 안정적인 학습 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;정보 보존&lt;/b&gt;: 원본 정보가 네트워크를 통과하며 유지됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 디코더&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디코더는 인코더가 이해한 정보를 바탕으로 실제 텍스트를 생성하는 역할을 합니다. 인코더와 유사하지만 두 가지 중요한 차이점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1065&quot; data-origin-height=&quot;1318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/duajvD/btsNlJ5xds1/5dFIrlwMqFJQjEqDRuBg70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/duajvD/btsNlJ5xds1/5dFIrlwMqFJQjEqDRuBg70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/duajvD/btsNlJ5xds1/5dFIrlwMqFJQjEqDRuBg70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FduajvD%2FbtsNlJ5xds1%2F5dFIrlwMqFJQjEqDRuBg70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;492&quot; height=&quot;609&quot; data-origin-width=&quot;1065&quot; data-origin-height=&quot;1318&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) 마스크 멀티 헤드 어텐션&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 적인 멀티 헤드 어텐션과 다르게 앞에 &quot;마스크&quot; 라는 키워드가 붙는데 이게 왜 붙는지 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 상황&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 텍스트 생성 시에는 이전까지 생성한 텍스트만 확인 가능한데, 학습할때 완선된 텍스트를 입력받음&lt;/li&gt;
&lt;li&gt;이때 미래에 등장할 단어를 미리 참조하면 안됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 이때&amp;nbsp;마스크를 사용해 특정 위치에서는 이전 위치의 단어만 볼 수 있도록 제한&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) 크로스 어텐션&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디코더의 또 다른 특별한 부분은 인코더의 정보를 활용하는 크로스 어텐션 메커니즘입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;작동 방식&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;쿼리(Q)&lt;/b&gt;: 디코더 자체의 잠재 상태에서 가져옴&lt;/li&gt;
&lt;li&gt;&lt;b&gt;키(K)와 값(V)&lt;/b&gt;: 인코더의 출력에서 가져옴&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 크로스 어텐션을 통해 디코더가 생성하는 각 단어가 입력 텍스트의 어떤 부분에 집중해야하는지 알 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;LLM을 활용한 실전 AI 애플리케이션 개발 - 허정준&lt;/span&gt;&lt;/p&gt;</description>
      <category>AI/DeepLearning</category>
      <category>transformer</category>
      <category>디코더</category>
      <category>마스크 멀티 헤드 어텐션</category>
      <category>어텐션</category>
      <category>인코더</category>
      <category>크로스 어텐션</category>
      <category>트랜스포머</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/94</guid>
      <comments>https://striver.tistory.com/entry/Transformer-%EC%9D%B8%EC%BD%94%EB%8D%94%EC%99%80-%EB%94%94%EC%BD%94%EB%8D%94#entry94comment</comments>
      <pubDate>Tue, 15 Apr 2025 21:46:06 +0900</pubDate>
    </item>
    <item>
      <title>[Transformer] 어텐션 연산과 핵심 구성 요소</title>
      <link>https://striver.tistory.com/entry/Transformer-%EC%96%B4%ED%85%90%EC%85%98-%EC%97%B0%EC%82%B0%EA%B3%BC-%ED%95%B5%EC%8B%AC-%EA%B5%AC%EC%84%B1-%EC%9A%94%EC%86%8C</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 어텐션 연산&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;우리는 글을 읽을 때 모든 단어에 동일한 중요도를 부여하지 않습니다. 어떤 단어들은 특별히 주목하고, 다른 단어들은 상대적으로 덜 집중하며 전체 맥락을 파악합니다. 트랜스포머 모델의 핵심인 어텐션 연산은 이러한 인간의 읽기 방식을 모방한 연산입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;1) 어텐션이란 무엇인가?&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;어텐션은 단어들 사이의 관계를 파악하여 문맥을 이해하는 방식으로, 다음과 같은 과정으로 작동합니다:&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;단어와 단어 사이의 관계를 계산하여 관련성의 깊이를 판단&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;관련이 깊은 단어는 더 많이, 관련이 적은 단어는 더 적게 맥락에 반영&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이 연산은 모델이 문장 내에서 단어 간의 복잡한 관계를 이해하고, 문맥에 따라 단어의 의미를 적절히 해석할 수 있게 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. QKV(쿼리, 키, 값) 모델 이해하기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;트랜스포머 아키텍처를 개발한 연구진은 정보 검색 분야에서 영감을 받아 쿼리(Query), 키(Key), 값(Value)이라는 개념을 도입했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;1) QKV 모델의 역할&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;구성 요소 역할 검색 시스템 예시&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 80px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;역할&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;설명&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;검색 예시&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;쿼리(Query)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;우리가 찾고자 하는 정보&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;사용자의 검색어&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;키(Key)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;쿼리와의 관련성을 판단하기 위한 특징&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;문서의 제목, 저자&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;값(Value)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;실제로 활용하게 될 정보&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;문서의 본문 내용&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이 구조에서 모델은 쿼리와 키의 관련도를 계산한 뒤, 가장 관련이 깊은 키에 연결된 값을 활용합니다. 이를 통해 맥락에 따라 단어의 의미를 다르게 해석할 수 있게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;2) 규칙 기반 방법의 한계&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;어텐션을 구현하기 위한 초기 시도로, 몇 가지 규칙 기반 방법들이 있었습니다:&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;모든 단어를 동등하게 반영하는 방식&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;문제점: 정보의 우선순위를 표현할 수 없음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;거리 기반 가중치 적용&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;가정: &quot;가까운 단어일수록 관련이 깊다&quot;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;문제점: 문맥과 무관하게 단순 거리만으로 의미 관계를 결정하는 한계&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이러한 규칙 기반 방법들의 핵심 문제는 유연성 부족입니다. 예를 들어, &quot;파리&quot;를 &quot;박물관&quot;으로 바꿔도 동일한 계산 결과가 나온다면, 문맥에 따른 의미 변화를 포착할 수 없습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;3) 벡터 기반 어텐션의 등장&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이러한 한계를 극복하기 위해, 트랜스포머는 토큰의 임베딩 벡터를 활용한 계산 방식을 도입했습니다. 벡터 간 연산을 통해 관련도를 계산하면 문자열이 일치하지 않더라도 의미적으로 유사한 정보를 활용할 수 있습니다. &lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;하지만 이 방식에도 문제가 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;쿼리와 키의 임베딩이 동일할 경우, 관련도가 지나치게 높게 계산되어 주변 맥락 반영이 미흡해질 수 있음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;직접적인 관련성은 잘 포착하지만, 문법적 관계와 같은 간접적 관련성은 반영하기 어려움&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;4) 가중치 도입을 통한 문제 해결&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;트랜스포머는 토큰 임베딩을 변환하는 학습 가능한 가중치를 도입하여 이러한 문제를 해결했습니다:&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;쿼리-키 임베딩 일치 문제 해결: 서로 다른 가중치 행렬을 통해 동일한 임베딩도 다르게 변환&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;간접 관계 반영: 가중치 행렬이 단어의 위치, 역할, 문장 구조 등을 학습하여 간접적 관계도 포착&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;결과적으로, 트랜스포머는 QKV 가중치를 통해 문맥에 따라 단어 간 관계를 유연하게 계산하고 적절히 반영하는 방법을 학습합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;5) 스케일 점곱 방식&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&quot;Attention is All You Need&quot; 논문에서 처음 소개된 스케일 점곱 어텐션의 연산 과정은 다음과 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;쿼리와 키를 곱한 후, 임베딩 차원의 제곱근으로 나눔 (분산 증가 방지)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;계산된 스코어에 소프트맥스를 적용하여 합이 1이 되는 가중치로 변환&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이 가중치와 값을 곱해 최종 출력 계산&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 멀티 헤드 어텐션&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;한 번의 어텐션 연산으로는 문장 내 복잡한 관계를 모두 포착하기 어렵습니다. 트랜스포머는 여러 개의 어텐션을 동시에 계산하는 '멀티 헤드 어텐션'을 도입해 이 문제를 해결했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;1) 멀티 헤드 어텐션의 작동 원리&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;쿼리, 키, 값을 헤드 수만큼 분할하여 각각의 어텐션 연산 수행&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;각 헤드의 결과를 결합하여 입력과 동일한 형태로 변환&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;선형층을 통과시켜 최종 결과 생성&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이를 통해 모델은 하나의 문장에서 다양한 관계와 패턴을 동시에 파악할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4. 트랜스포머의 핵심 구성 요소&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;1) 층 정규화(Layer Normalization)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;딥러닝 모델에서 데이터가 일정한 분포를 유지하도록 만드는 정규화는 학습 안정성과 속도에 중요한 영향을 미칩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;(1) 배치 정규화 vs 층 정규화&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;배치 정규화&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;배치 내 데이터 간 정규화 수행&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이미지 처리에 주로 사용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;문제점: 자연어 처리에서는 문장 길이가 다양하여 효과적이지 않음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1315&quot; data-origin-height=&quot;1231&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btJWsi/btsNlDPFJ05/XkRBO4IdQ5PR6tHKWoqmG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btJWsi/btsNlDPFJ05/XkRBO4IdQ5PR6tHKWoqmG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btJWsi/btsNlDPFJ05/XkRBO4IdQ5PR6tHKWoqmG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtJWsi%2FbtsNlDPFJ05%2FXkRBO4IdQ5PR6tHKWoqmG0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1315&quot; height=&quot;1231&quot; data-origin-width=&quot;1315&quot; data-origin-height=&quot;1231&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;층 정규화&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;특정 차원에서 정규화 수행&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;각 토큰 임베딩의 평균과 표준편차를 구해 정규화&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;자연어 처리에 적합&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1599&quot; data-origin-height=&quot;1021&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dulq1M/btsNkvyYO8a/F39fBQLZPwddsuVolAbXDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dulq1M/btsNkvyYO8a/F39fBQLZPwddsuVolAbXDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dulq1M/btsNkvyYO8a/F39fBQLZPwddsuVolAbXDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdulq1M%2FbtsNkvyYO8a%2FF39fBQLZPwddsuVolAbXDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1599&quot; height=&quot;1021&quot; data-origin-width=&quot;1599&quot; data-origin-height=&quot;1021&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;(2) 정규화 적용 방식&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;사후 정규화: 원 트랜스포머 논문 방식 - 어텐션과 피드 포워드 층 이후에 정규화 적용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;사전 정규화: 최신 방식 - 먼저 정규화 적용 후 다른 층 통과 (학습 안정성 향상)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;2) 피드 포워드 층(Feed-Forward Layer)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;트랜스포머에서 피드 포워드 층은 데이터의 특징을 학습하는 완전 연결 층으로, 다음과 같은 역할을 합니다:&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;멀티 헤드 어텐션이 단어 간 관계를 파악한다면, 피드 포워드 층은 텍스트 전체 맥락 이해 담당&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;선형 층, 드롭아웃 층, 층 정규화, 활성 함수로 구성&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;모델이 입력 텍스트의 고수준 특성과 의미를 포착할 수 있도록 함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;LLM을 활용한 실전 AI 애플리케이션 개발 - 허정준&lt;/span&gt;&lt;/p&gt;</description>
      <category>AI/DeepLearning</category>
      <category>Deeplearning</category>
      <category>transformer</category>
      <category>배치 정규화</category>
      <category>사후 정규화</category>
      <category>셀프 어텐션</category>
      <category>어텐션</category>
      <category>정규화</category>
      <category>층 정규화</category>
      <category>트랜스포머</category>
      <category>피드 포워 층</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/93</guid>
      <comments>https://striver.tistory.com/entry/Transformer-%EC%96%B4%ED%85%90%EC%85%98-%EC%97%B0%EC%82%B0%EA%B3%BC-%ED%95%B5%EC%8B%AC-%EA%B5%AC%EC%84%B1-%EC%9A%94%EC%86%8C#entry93comment</comments>
      <pubDate>Mon, 14 Apr 2025 22:32:13 +0900</pubDate>
    </item>
    <item>
      <title>[Text Embedding] 텍스트를 임베딩하는 3단계</title>
      <link>https://striver.tistory.com/entry/Input-Embedding-%ED%85%8D%EC%8A%A4%ED%8A%B8%EB%A5%BC-%EC%9E%84%EB%B2%A0%EB%94%A9%ED%95%98%EB%8A%94-3%EB%8B%A8%EA%B3%84</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;딥러닝 모델이 텍스트를 이해하려면 먼저 &lt;b&gt;문자 기반의 텍스트 데이터를 숫자 기반의 벡터&lt;/b&gt;로 변환해야 합니다. 이 과정을 임베딩(embedding)이라고 부르며, 보통 다음의 세 가지 단계를 거칩니다.&lt;/p&gt;
&lt;h3 data-end=&quot;260&quot; data-start=&quot;236&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 토큰화 (Tokenization)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-end=&quot;370&quot; data-start=&quot;262&quot; data-ke-size=&quot;size16&quot;&gt;텍스트를 모델에 넣기 위해 가장 먼저 해야 할 일은 &lt;b&gt;텍스트를 잘게 나누는 것&lt;/b&gt;, 즉 토큰화입니다. 이때 각 토큰에 고유한 숫자 ID를 부여하며, 이 정보를 사전(dict)에 기록합니다.&lt;/p&gt;
&lt;p data-end=&quot;407&quot; data-start=&quot;372&quot; data-ke-size=&quot;size16&quot;&gt;토큰화 방법은 다양하지만, 크게 다음 두 가지 방식이 있습니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;608&quot; data-start=&quot;409&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;520&quot; data-start=&quot;409&quot;&gt;&lt;b&gt;큰 단위 토큰화&lt;/b&gt;: 예를 들어 단어 단위로 자르는 방식입니다. 의미 보존에 유리하지만, 새로운 단어가 등장했을 때 처리할 수 없는 OOV(Out-Of-Vocabulary) 문제가 발생합니다.&lt;/li&gt;
&lt;li data-end=&quot;608&quot; data-start=&quot;521&quot;&gt;&lt;b&gt;작은 단위 토큰화&lt;/b&gt;: 예를 들어 글자 단위로 자르는 방식입니다. OOV 문제는 줄지만, 의미가 잘려 나가 문장의 의미를 제대로 전달하기 어렵습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;801&quot; data-start=&quot;610&quot; data-ke-size=&quot;size16&quot;&gt;그래서 최근에는 두 방식의 장점을 적절히 결합한 &lt;b&gt;서브워드(Subword) 토크나이저&lt;/b&gt;를 많이 사용합니다. 이 방식은 자주 등장하는 단어는 그대로 유지하고, 드물게 등장하는 단어는 의미 있는 작은 조각으로 분해합니다. 대표적인 예로는 BPE(Byte Pair Encoding), WordPiece, SentencePiece 등이 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;801&quot; data-start=&quot;610&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;838&quot; data-start=&quot;808&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 토큰 임베딩 (Token Embedding)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-end=&quot;927&quot; data-start=&quot;840&quot; data-ke-size=&quot;size16&quot;&gt;토큰화된 결과는 단지 정수 ID일 뿐, 단어의 의미를 표현하기엔 부족합니다. 따라서 각 토큰 ID를 &lt;b&gt;다차원 벡터&lt;/b&gt;로 바꿔주는 임베딩 과정이 필요합니다. 이때 사용되는 것이 바로 딥러닝 프레임워크의 Embedding 레이어입니다. 파이토치(PyTorch)에서는 다음과 같이 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;1009&quot; data-start=&quot;929&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1744356961189&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import torch
import torch.nn as nn

embedding_dim = 16
embed_layer = nn.Embedding(len(str2idx), embedding_dim)

input_embeddings = embed_layer(torch.tensor(input_ids))  # (5, 16)
input_embeddings = input_embeddings.unsqueeze(0)  # (1, 5, 16)
input_embeddings.shape&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-end=&quot;1424&quot; data-start=&quot;1288&quot; data-ke-size=&quot;size16&quot;&gt;여기서 주의할점은 초기&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;벡터로&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;만드는&amp;nbsp;방법도&amp;nbsp;함께&amp;nbsp;학습합니다. 이것이 바로 딥러닝이 머신러닝과의 차별점 중 하나 입니다.&lt;/p&gt;
&lt;p data-end=&quot;1424&quot; data-start=&quot;1288&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1464&quot; data-start=&quot;1431&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 위치 인코딩&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;RNN과&amp;nbsp;트랜스포머의&amp;nbsp;가장&amp;nbsp;큰&amp;nbsp;차이점은&amp;nbsp;입력&amp;nbsp;처리&amp;nbsp;방식에&amp;nbsp;있습니다.&amp;nbsp;RNN은&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;순서&amp;nbsp;정보가&amp;nbsp;소실됩니다.&amp;nbsp;텍스트에서&amp;nbsp;순서는&amp;nbsp;중요한&amp;nbsp;정보이므로&amp;nbsp;위치&amp;nbsp;인코딩을&amp;nbsp;통해&amp;nbsp;이를&amp;nbsp;보완합니다.&lt;br /&gt;&lt;br /&gt;초기에는&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;데이터를&amp;nbsp;통해&amp;nbsp;학습하는&amp;nbsp;방식을&amp;nbsp;많이&amp;nbsp;활용합니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;절대 위치 인코딩&lt;/b&gt;은 구현이 간단하지만, 토큰 간의 상대적 위치 정보를 활용하지 못하고 긴 텍스트 추론 시 성능이 저하되는 단점이 있습니다. 이러한 단점을 보완하기 위해 최근에는 &lt;b&gt;상대적 위치 인코딩&lt;/b&gt;도 함께 사용합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1836&quot; data-start=&quot;1824&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4. 최종적인 입력 임베딩 생성 과정&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-end=&quot;1874&quot; data-start=&quot;1838&quot; data-ke-size=&quot;size16&quot;&gt;최종적으로 모델에 입력되는 임베딩은 다음과 같은 과정을 거칩니다.&lt;/p&gt;
&lt;p data-end=&quot;1874&quot; data-start=&quot;1838&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1008&quot; data-origin-height=&quot;1396&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/REunI/btsNhPxVdIb/iPK4GDVztBkAGCKDipKRg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/REunI/btsNhPxVdIb/iPK4GDVztBkAGCKDipKRg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/REunI/btsNhPxVdIb/iPK4GDVztBkAGCKDipKRg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FREunI%2FbtsNhPxVdIb%2FiPK4GDVztBkAGCKDipKRg0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;276&quot; height=&quot;382&quot; data-origin-width=&quot;1008&quot; data-origin-height=&quot;1396&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;1874&quot; data-start=&quot;1838&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1874&quot; data-start=&quot;1838&quot; data-ke-size=&quot;size16&quot;&gt;1) 텍스트를 토큰화하여 토큰 ID와 위치 ID를 생성합니다.&lt;/p&gt;
&lt;p data-end=&quot;1874&quot; data-start=&quot;1838&quot; data-ke-size=&quot;size16&quot;&gt;2) 각 ID를 각각 토큰 임베딩과 위치 임베딩으로 변환합니다.&lt;/p&gt;
&lt;p data-end=&quot;1874&quot; data-start=&quot;1838&quot; data-ke-size=&quot;size16&quot;&gt;3) 두 임베딩을 더해 최종 입력 임베딩을 만듭니다.&lt;/p&gt;
&lt;p data-end=&quot;1874&quot; data-start=&quot;1838&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2045&quot; data-start=&quot;1978&quot; data-ke-size=&quot;size16&quot;&gt;이 과정을 통해 모델은 단어의 의미뿐 아니라 &lt;b&gt;단어가 문장에서 어떤 위치에 있는지&lt;/b&gt;까지 함께 고려할 수 있게 됩니다.&lt;/p&gt;
&lt;p data-end=&quot;2045&quot; data-start=&quot;1978&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;2045&quot; data-start=&quot;1978&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;LLM을 활용한 실전 AI 애플리케이션 개발 - 허정준&lt;/span&gt;&lt;/p&gt;</description>
      <category>AI/DeepLearning</category>
      <category>Embedding</category>
      <category>token</category>
      <category>token embedding</category>
      <category>모델 텍스트 입력</category>
      <category>위치 인코딩</category>
      <category>토큰화</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/92</guid>
      <comments>https://striver.tistory.com/entry/Input-Embedding-%ED%85%8D%EC%8A%A4%ED%8A%B8%EB%A5%BC-%EC%9E%84%EB%B2%A0%EB%94%A9%ED%95%98%EB%8A%94-3%EB%8B%A8%EA%B3%84#entry92comment</comments>
      <pubDate>Fri, 11 Apr 2025 22:44:49 +0900</pubDate>
    </item>
    <item>
      <title>[Transformer] RNN과 트랜스포머 아키텍처의 이해</title>
      <link>https://striver.tistory.com/entry/Transformer-RNN%EA%B3%BC-%ED%8A%B8%EB%9E%9C%EC%8A%A4%ED%8F%AC%EB%A8%B8-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EC%9D%98-%EC%9D%B4%ED%95%B4</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;딥러닝 기반 자연어 처리 기술은 최근 몇 년간 혁신적인 발전을 이루었습니다. 이 발전의 중심에는 RNN(순환신경망)에서 트랜스포머 아키텍처로의 패러다임 전환이 있었습니다. 이번 글에서는 두 모델의 특징과 차이점, 그리고 트랜스포머가 어떻게 기존 RNN의 한계를 극복했는지 알아보겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 순환신경망(RNN)의 구조와 한계&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RNN은 시퀀스 데이터를 처리하기 위한 초기 딥러닝 모델로, 텍스트를 순차적으로 하나씩 처리하는 특징을 가집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1363&quot; data-origin-height=&quot;427&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7fQNH/btsNhPbFTvh/YI4EKQIn2pC65AJYCv5IIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7fQNH/btsNhPbFTvh/YI4EKQIn2pC65AJYCv5IIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7fQNH/btsNhPbFTvh/YI4EKQIn2pC65AJYCv5IIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7fQNH%2FbtsNhPbFTvh%2FYI4EKQIn2pC65AJYCv5IIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1363&quot; height=&quot;427&quot; data-origin-width=&quot;1363&quot; data-origin-height=&quot;427&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) RNN의 작동 방식&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;텍스트의 각 토큰(단어)을 하나씩 순차적으로 입력&lt;/li&gt;
&lt;li&gt;이전 토큰 처리 결과를 다음 토큰 처리에 활용&lt;/li&gt;
&lt;li&gt;이런 순환 구조가 시퀀스 데이터 처리에 적합&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) RNN의 한계점&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;순차적 처리로 인한 비효율&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;병렬 처리가 불가능해 학습 속도가 느림&lt;/li&gt;
&lt;li&gt;긴 시퀀스 처리 시 계산 시간이 크게 증가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장기 의존성 문제&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력이 길어질수록 초기 정보가 희석되는 현상 발생&lt;/li&gt;
&lt;li&gt;텍스트의 앞부분과 뒷부분의 관계 파악이 어려움&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;깊은 네트워크 구성의 어려움&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;층을 깊게 쌓으면 그래디언트 소실이나 폭발 문제 발생&lt;/li&gt;
&lt;li&gt;학습의 불안정성 증가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LSTM이나 GRU와 같은 개선된 RNN 모델들이 등장했지만, 근본적인 한계를 완전히 극복하지는 못했습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 트랜스포머 아키텍처&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜스포머는 2017년 &quot;Attention is All You Need&quot; 논문을 통해 소개된 혁신적인 아키텍처로, RNN의 한계를 극복하기 위해 설계되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1259&quot; data-origin-height=&quot;1449&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mUobo/btsNfFoANta/xwQHJpeaBg6UT0Mlowy9N0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mUobo/btsNfFoANta/xwQHJpeaBg6UT0Mlowy9N0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mUobo/btsNfFoANta/xwQHJpeaBg6UT0Mlowy9N0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmUobo%2FbtsNfFoANta%2FxwQHJpeaBg6UT0Mlowy9N0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1259&quot; height=&quot;1449&quot; data-origin-width=&quot;1259&quot; data-origin-height=&quot;1449&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) 트랜스포머의 핵심 구성 요소&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인코더&lt;/b&gt;: 언어를 이해하는 역할&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;디코더&lt;/b&gt;: 언어를 생성하는 역할&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) 트랜스포머의 장점&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;뛰어난 확장성&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;더 깊은 모델 구성이 가능하며 학습도 안정적&lt;/li&gt;
&lt;li&gt;동일한 블록의 반복적 사용으로 구조 확장이 용이&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;우수한 효율성&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;병렬 연산이 가능해 학습 시간이 크게 단축&lt;/li&gt;
&lt;li&gt;자원 활용 효율이 높아 대규모 모델 학습 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;긴 시퀀스 처리 능력&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력 시퀀스가 길어져도 성능 저하가 적음&lt;/li&gt;
&lt;li&gt;문서 전체 맥락을 파악하는 능력이 우수&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 셀프 어텐션&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜스포머의 가장 중요한 혁신은 순차적 처리 방식을 버리고 '셀프 어텐션' 메커니즘을 도입한 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;셀프 어텐션의 작동 원리&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문장 내 모든 단어 간의 관련성을 동시에 계산&lt;/li&gt;
&lt;li&gt;각 단어가 다른 모든 단어와 어떤 관계를 갖는지 학습&lt;/li&gt;
&lt;li&gt;관련성에 따라 각 단어의 표현을 조정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 문맥을 더 정확하게 이해하고, 장거리 의존성 문제를 효과적으로 해결합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4. 트랜스포머의 처리 과정&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜스포머는 다음과 같은 단계로 텍스트를 처리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) 입력 임베딩&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;텍스트를 숫자 벡터(임베딩)로 변환&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) 위치 인코딩&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;순서 정보를 임베딩에 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3) 인코더 처리&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;층 정규화, 멀티 헤드 어텐션, 피드 포워드 층을 통한 처리&lt;/li&gt;
&lt;li&gt;입력 텍스트의 의미 이해&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;4) 디코더 처리&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;마스크 멀티 헤드 어텐션으로 자기 자신 처리&lt;/li&gt;
&lt;li&gt;크로스 어텐션으로 인코더 결과 활용&lt;/li&gt;
&lt;li&gt;최종 출력 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;5. 인코더와 디코더의 주요 차이점&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) 마스크 멀티 헤드 어텐션&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디코더에서는 미래 정보를 사용하지 못하도록 마스킹이 적용됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;필요성&lt;/b&gt;: 실제 텍스트 생성 시 미래 토큰은 알 수 없기 때문&lt;/li&gt;
&lt;li&gt;&lt;b&gt;작동 방식&lt;/b&gt;: 현재 위치 이후의 토큰에는 어텐션 점수를 0으로 설정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;효과&lt;/b&gt;: 자연스러운 텍스트 생성 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) 크로스 어텐션&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디코더에서 인코더의 출력을 활용하는 메커니즘입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;역할&lt;/b&gt;: 인코더가 이해한 입력 정보를 디코더의 생성 과정에 반영&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장점&lt;/b&gt;: 입력 텍스트의 맥락을 출력 생성에 효과적으로 반영 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜스포머 아키텍처는 RNN의 한계를 극복하고 자연어 처리 성능을 획기적으로 향상시켰습니다. 특히 셀프 어텐션 메커니즘을 통해 병렬 처리가 가능해지고, 장거리 의존성 문제를 해결했습니다. 이러한 혁신은 BERT, GPT와 같은 강력한 언어 모델의 기반이 되었고, 현대 NLP 기술의 중심에 자리 잡고 있습니다. 다음 포스팅에서는 트랜스포머의 인코더와 디코더를 구성하는 각 Layer에 대해 자세히 알아보고, 멀티 헤드 어텐션, 피드 포워드 네트워크, 층 정규화 등의 구성 요소가 어떻게 작동하는지 심층적으로 살펴보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;LLM을 활용한 실전 AI 애플리케이션 개발 - 허정준&lt;/span&gt;&lt;/p&gt;</description>
      <category>AI/DeepLearning</category>
      <category>rnn</category>
      <category>transformer</category>
      <category>디코더</category>
      <category>셀프 어텐션</category>
      <category>어텐션</category>
      <category>인코더</category>
      <category>트랜스포머</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/91</guid>
      <comments>https://striver.tistory.com/entry/Transformer-RNN%EA%B3%BC-%ED%8A%B8%EB%9E%9C%EC%8A%A4%ED%8F%AC%EB%A8%B8-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EC%9D%98-%EC%9D%B4%ED%95%B4#entry91comment</comments>
      <pubDate>Thu, 10 Apr 2025 20:12:08 +0900</pubDate>
    </item>
    <item>
      <title>[GitHub] Fork &amp;amp; Pull Request</title>
      <link>https://striver.tistory.com/entry/GitHub-Fork-Pull-Request</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 프로젝트나 제가 지금까지 진행한 프로젝트에서는 Github에서 프로젝트를 관리할때 각자 기능별 브랜치를 만들고 메인 브랜치에 반영하고 싶으면 로컬에서 commit, merge 를 통해 검증이 되면 그 main 브랜치를 원격 레파지토리에 push해서 병합하는 구조를 사용했었습니다. 그런데 이번에 새로운 프로젝트를 진행하면서 Fork + Pull Request 조합으로 프로젝트를 관리하게 돼서 이 과정에 대해 알아보려고합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. Fork란 무엇인가?&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Fork&lt;/b&gt;는 다른 사용자의 GitHub 저장소(Repository)를 내 계정으로 복제하는 기능입니다. 이는 단순한 복사본이 아니라 원본 저장소와의 연결을 유지하면서 독립적으로 작업할 수 있는 공간을 만들어줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Fork의 주요 목적&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오픈소스 프로젝트 기여: 직접적인 쓰기 권한 없이도 프로젝트에 기여할 수 있습니다&lt;/li&gt;
&lt;li&gt;독립적인 개발: 기존 코드를 기반으로 새로운 기능을 추가하거나 방향을 전환할 수 있습니다&lt;/li&gt;
&lt;li&gt;안전한 실험: 원본 코드에 영향을 주지 않고 다양한 변경사항을 시도해볼 수 있습니다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. Pull Request란?&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Pull Request(PR)&lt;/b&gt;는 내가 Fork한 저장소에서 작업한 변경사항을 &lt;b&gt;원본 저장소에 반영&lt;/b&gt;해달라고 요청하는 기능입니다. 코드 리뷰와 토론을 거쳐 최종적으로 프로젝트에 병합될 수 있습니다. PR의 장점은 코드 변경사항을 한눈에 비교할 수 있어 리뷰어가 쉽게 검토할 수 있다는 점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. Fork &amp;amp; Pull Request 작업 흐름&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Fork와 Pull Request를 활용한 전체 작업 흐름을 단계별로 살펴보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) Fork 하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 기여하고 싶은 프로젝트의 GitHub 페이지에서 오른쪽 상단의 'Fork' 버튼을 클릭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1799&quot; data-origin-height=&quot;219&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Jhhdq/btsNd9WTCvi/HHTGHOqRhmzCuoS85Grmjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Jhhdq/btsNd9WTCvi/HHTGHOqRhmzCuoS85Grmjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Jhhdq/btsNd9WTCvi/HHTGHOqRhmzCuoS85Grmjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJhhdq%2FbtsNd9WTCvi%2FHHTGHOqRhmzCuoS85Grmjk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1799&quot; height=&quot;219&quot; data-origin-width=&quot;1799&quot; data-origin-height=&quot;219&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fork가 완료되면 내 GitHub 계정에 동일한 이름의 저장소가 생성됩니다. 이제 이 저장소는 내 소유이며 자유롭게 수정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1247&quot; data-origin-height=&quot;71&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBM2Js/btsNezU7Cv8/iAIYdxkiFcIEtc8Q3D3XD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBM2Js/btsNezU7Cv8/iAIYdxkiFcIEtc8Q3D3XD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBM2Js/btsNezU7Cv8/iAIYdxkiFcIEtc8Q3D3XD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBM2Js%2FbtsNezU7Cv8%2FiAIYdxkiFcIEtc8Q3D3XD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1247&quot; height=&quot;71&quot; data-origin-width=&quot;1247&quot; data-origin-height=&quot;71&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) Clone 및 Remote 설정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fork한 저장소를 로컬 환경으로 가져옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;crmsh&quot; style=&quot;color: #383a42; text-align: left;&quot;&gt;&lt;code&gt;# 내 Fork한 저장소 복제
git clone https://github.com/내사용자명/저장소이름.git

# 복제된 디렉토리로 이동
cd 저장소이름&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 로컬 저장소에 원본 저장소를 'upstream'이라는 이름으로 추가합니다. 이는 나중에 원본 저장소의 변경사항을 쉽게 가져오기 위함입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;vala&quot; style=&quot;color: #383a42; text-align: left;&quot;&gt;&lt;code&gt;# 원본 저장소를 'upstream'으로 추가
git remote add upstream https://github.com/원본사용자명/저장소이름.git

# 설정된 Remote 확인
git remote -v&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 결과는 다음과 같을 것입니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;awk&quot; style=&quot;color: #383a42; text-align: left;&quot;&gt;&lt;code&gt;origin    https://github.com/내사용자명/저장소이름.git (fetch)
origin    https://github.com/내사용자명/저장소이름.git (push)
upstream  https://github.com/원본사용자명/저장소이름.git (fetch)
upstream  https://github.com/원본사용자명/저장소이름.git (push)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3) 브랜치 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업을 시작하기 전에 새로운 브랜치를 만들어 작업합니다. 이는 여러 기능을 동시에 개발하거나 다른 팀원과의 코드 충돌을 방지하는데 도움이 됩니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;vala&quot; style=&quot;color: #383a42; text-align: left;&quot;&gt;&lt;code&gt;# 새 브랜치 생성 및 전환
git checkout -b feature/new-feature

# 브랜치 목록 확인
git branch&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;4) 코드 수정 및 커밋&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 필요한 변경사항을 작업하고 커밋합니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;bash&quot; style=&quot;color: #383a42; text-align: left;&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;# 파일 수정 후 변경사항 스테이징
git add .

# 변경사항 커밋
git commit -m &quot;[feat] Retrieve docs node&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;5) Fork한 저장소에 Push&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업이 완료되면 내 GitHub 계정의 Fork 저장소에 변경사항을 Push합니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;maxima&quot; style=&quot;color: #383a42; text-align: left;&quot;&gt;&lt;code&gt;git push origin feature/new-feature&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;6) Pull Request 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub로 돌아가면 방금 Push한 브랜치에 대한 &lt;b&gt;'Compare &amp;amp; pull request'&lt;/b&gt; 버튼이 표시됩니다. 이 버튼을 클릭하여 Pull Request를 생성합니다. PR 생성 페이지에서 PR의 제목과 설명을 작성합니다. 변경사항을 설명하고 필요한 경우 관련 이슈를 언급합니다. 생성된 PR 페이지에서는 변경된 코드를 한눈에 비교해볼 수 있습니다. 이는 코드 리뷰를 진행하는 데 매우 유용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;7) 코드 리뷰 및 병합&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PR이 생성되면 프로젝트 관리자나 다른 팀원들이 코드를 리뷰하고 피드백을 제공합니다. 필요한 경우 추가 수정을 진행하고 다시 Push합니다.모든 검토가 완료되면 관리자가 'Merge pull request' 버튼을 클릭하여 변경사항을 원본 저장소에 병합합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;8) 동기화 및 정리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PR이 병합된 후에는 로컬 저장소를 원본 저장소와 동기화하고 사용한 브랜치를 정리합니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;vala&quot; style=&quot;color: #383a42; text-align: left;&quot;&gt;&lt;code&gt;# 메인 브랜치로 전환
git checkout main

# 원본 저장소의 변경사항 가져오기
git pull upstream main

# 로컬 브랜치 삭제
git branch -d feature/new-feature

# Fork 저장소도 업데이트
git push origin main&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Fork &amp;amp; Pull Request의 장점&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;코드 품질 향상&lt;/b&gt;: 코드 리뷰 과정을 통해 버그를 사전에 발견하고 코드 품질을 높일 수 있습니다&lt;/li&gt;
&lt;li&gt;&lt;b&gt;지식 공유&lt;/b&gt;: 팀원들이 서로의 코드를 검토하며 지식을 공유할 수 있습니다&lt;/li&gt;
&lt;li&gt;&lt;b&gt;프로젝트 안정성&lt;/b&gt;: 직접적인 main 브랜치 수정 없이 변경사항을 검증할 수 있습니다&lt;/li&gt;
&lt;li&gt;&lt;b&gt;기여 추적&lt;/b&gt;: 누가 어떤 기능을 개발했는지 명확하게 기록됩니다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fork와 Pull Request는 오픈소스 프로젝트뿐만 아니라 팀 프로젝트에서도 효과적인 협업 방식입니다. 처음에는 기존의 브랜치 방식보다 복잡해 보일 수 있지만, 익숙해지면 더 체계적이고 안전한 개발 환경을 구축할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dusanbaek.tistory.com/97&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://dusanbaek.tistory.com/97&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@0x45c/Git-Fork-Pull-Request&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@0x45c/Git-Fork-Pull-Request&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Dev/Git&amp;amp;GitHub</category>
      <category>fork</category>
      <category>git</category>
      <category>github</category>
      <category>pull request</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/89</guid>
      <comments>https://striver.tistory.com/entry/GitHub-Fork-Pull-Request#entry89comment</comments>
      <pubDate>Wed, 9 Apr 2025 22:08:20 +0900</pubDate>
    </item>
    <item>
      <title>[DeepLearning] 메모리 효율적인 딥러닝</title>
      <link>https://striver.tistory.com/entry/DeepLearning-%EB%A9%94%EB%AA%A8%EB%A6%AC-%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9D%B8-%EB%94%A5%EB%9F%AC%EB%8B%9D</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;딥러닝 모델, 특히 대규모 언어 모델(LLM)이 발전함에 따라 모델 크기와 필요한 계산 자원도 크게 증가했습니다. 하지만 모델 성능이 향상됨에 따라 모델의 크기와 요구되는 계산 자원 또한 기하급수적으로 증가하고 있습니다. 수십억에서 수백억 개의 파라미터를 가진 모델을 학습하고 추론하려면, 단순히 고성능 하드웨어만으로는 한계가 있습니다. 메모리 효율성과 연산 최적화 없이는 현실적인 운영이 어려워지기 때문이죠. 이번 글에서는 대규모 딥러닝 모델을 효율적으로 다루기 위한 핵심 기술들을 정리해보겠습니다. 특히 메모리 관점에서 모델을 어떻게 경량화하고 최적화할 수 있는지를 중심으로 다뤄보겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 데이터 타입에 따른 메모리 사용량&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딥러닝 모델은 본질적으로 수많은 파라미터(가중치)를 포함한 거대한 행렬 연산의 집합입니다. 이 파라미터들을 어떤 데이터 타입으로 저장하느냐에 따라 모델의 용량과 성능이 크게 달라집니다.&lt;/p&gt;
&lt;p data-end=&quot;695&quot; data-start=&quot;609&quot; data-ke-size=&quot;size16&quot;&gt;과거에는 32비트 부동소수점(FP32)이 표준이었지만, 최근에는 메모리와 연산 효율을 고려해 FP16이나 BF16과 같은 16비트 형식이 널리 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 71.628%; height: 148px;&quot; border=&quot;1&quot; data-end=&quot;909&quot; data-start=&quot;697&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.3795%; text-align: center;&quot;&gt;&lt;b&gt;데이터 타입&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 16.8229%; text-align: center;&quot;&gt;&lt;b&gt;정밀도&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 73.504%; text-align: center;&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;800&quot; data-start=&quot;752&quot;&gt;
&lt;td style=&quot;width: 16.3795%; text-align: center;&quot;&gt;FP32&lt;/td&gt;
&lt;td style=&quot;width: 16.8229%; text-align: center;&quot;&gt;높음&lt;/td&gt;
&lt;td style=&quot;width: 73.504%; text-align: center;&quot;&gt;높은 정밀도와 안정성, 큰 메모리 사용량&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;855&quot; data-start=&quot;801&quot;&gt;
&lt;td style=&quot;width: 16.3795%; text-align: center;&quot;&gt;FP16&lt;/td&gt;
&lt;td style=&quot;width: 16.8229%; text-align: center;&quot;&gt;낮음&lt;/td&gt;
&lt;td style=&quot;width: 73.504%; text-align: center;&quot;&gt;낮은 정밀도, 메모리 사용량 감소, 오버플로우 위험&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;909&quot; data-start=&quot;856&quot;&gt;
&lt;td style=&quot;width: 16.3795%; text-align: center;&quot;&gt;BF16&lt;/td&gt;
&lt;td style=&quot;width: 16.8229%; text-align: center;&quot;&gt;중간&lt;/td&gt;
&lt;td style=&quot;width: 73.504%; text-align: center;&quot;&gt;FP16보다 넓은 표현 범위, 학습 안정성이 높음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1744103446625&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;모델 용량 = 파라미터 수 &amp;times; 파라미터당 비트 수&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;10억 개 파라미터 모델(fp16): 20억B -&amp;gt; 2GB&lt;/li&gt;
&lt;li&gt;70억 개 파라미터 모델(fp16): 140억B -&amp;gt; 14GB&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;579&quot; data-origin-height=&quot;269&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KLxpM/btsNdHZFZsn/ssj6H02PcJ1mpfuotJ5wvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KLxpM/btsNdHZFZsn/ssj6H02PcJ1mpfuotJ5wvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KLxpM/btsNdHZFZsn/ssj6H02PcJ1mpfuotJ5wvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKLxpM%2FbtsNdHZFZsn%2Fssj6H02PcJ1mpfuotJ5wvK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;579&quot; height=&quot;269&quot; data-origin-width=&quot;579&quot; data-origin-height=&quot;269&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 양자화: 모델 압축의 핵심 기술&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;양자화는 더 나아가 모델의 정밀도를 줄이는 대신 크기를 획기적으로 줄이는 방법입니다. 32비트나 16비트가 아닌, 8비트 또는 4비트로 파라미터를 표현함으로써 모델의 메모리 사용량을 1/2에서 최대 1/8까지 절감할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;주요 양자화 방법&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 절대 최댓값 기준 양자화 (Absmax Quantization)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1338&quot; data-start=&quot;1308&quot;&gt;전체 데이터의 절대값 최대치를 기준으로 스케일링&lt;/li&gt;
&lt;li data-end=&quot;1359&quot; data-start=&quot;1342&quot;&gt;단순하지만 이상치에 민감&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) &lt;b&gt;블록 단위 양자화 (Block-wise Quantization)&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1467&quot; data-start=&quot;1409&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1437&quot; data-start=&quot;1409&quot;&gt;K개의 데이터를 하나의 블록으로 묶어 양자화&lt;/li&gt;
&lt;li data-end=&quot;1467&quot; data-start=&quot;1441&quot;&gt;이상치의 영향을 제한된 범위로 격리 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) &lt;b&gt;퀀타일(Quantile) 양자화&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1563&quot; data-start=&quot;1499&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1525&quot; data-start=&quot;1499&quot;&gt;데이터를 정렬해 균등 분포되도록 스케일링&lt;/li&gt;
&lt;li data-end=&quot;1563&quot; data-start=&quot;1529&quot;&gt;메모리와 계산량이 증가하지만 정밀도 손실을 최소화 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. GPU 메모리 사용 구조 이해&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딥러닝 학습 과정에서 GPU 메모리에는 다음과 같은 항목이 저장됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델 파라미터&lt;/li&gt;
&lt;li&gt;그레이디언트&lt;/li&gt;
&lt;li&gt;옵티마이저 상태&lt;/li&gt;
&lt;li&gt;순전파 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;이 중 순전파 상태는 역전파 시 다시 사용되기 때문에 전체 메모리에서 상당한 부분을 차지하게 됩니다. 특히 모델이 깊거나 배치 크기가 클수록 메모리 부담은 더욱 커집니다. 딥러닝 학습 과정을 간단히 요약하면, 먼저 순전파를 수행하고 그때 계산한 손실로부터 역전파를 수행하고 마지막으로 옵티마이저를 통해 모델을 업데이트합니다. 역전파는 순전파의 결과를 바탕으로 수행하는데, 이때 역전파를 수행하기 위해 저장하고 있는 값들이 순전파 상태값입니다. 그레이디언트는 역전파 결과 생성됩니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4. 메모리 효율적인 학습 기법&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) 그레이디언트 누적(Gradient Accumulation)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 배치의 그레이디언트를 누적한 후 모델을 한번에 업데이트하는 방식입니다.&lt;/li&gt;
&lt;li data-end=&quot;2077&quot; data-start=&quot;2050&quot;&gt;&lt;b&gt;장점&lt;/b&gt;: 적은 메모리로 큰 배치 효과&lt;/li&gt;
&lt;li data-end=&quot;2103&quot; data-start=&quot;2078&quot;&gt;&lt;b&gt;단점&lt;/b&gt;: 학습 시간이 길어질 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) 그레이디언트 체크포인팅(Gradient Checkpointing)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;순전파 시 모든 중간 값을 저장하지 않고 일부만 저장한 뒤, 역전파 시 필요할 때 재계산하는 방식입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장점&lt;/b&gt;: 메모리 사용량 대폭 감소&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단점&lt;/b&gt;: 일부 계산 재수행으로 학습 시간 증가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;5. 분산 학습 기법&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분산학습이란 대규모 모델 학습을 위해 여러 GPU를 활용하는 방법으로 모델 학습 속도를 높이고 1개의 GPU로 학습이 어려운 모델을 다루는 것을 목표로 하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) 데이터 병렬화(Data Parallelism)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 모델을 여러 GPU에 복제하고 각각 다른, 데이터를 처리하는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2514&quot; data-start=&quot;2477&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) 모델 병렬화 (Model Parallelism)&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;2561&quot; data-start=&quot;2516&quot; data-ke-size=&quot;size16&quot;&gt;모델 자체를 분할하여 각 GPU가 서로 다른 파트를 처리합니다. 다시 세분화하면:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2715&quot; data-start=&quot;2563&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2646&quot; data-start=&quot;2563&quot;&gt;&lt;b&gt;파이프라인 병렬화 (Pipeline Parallelism)&lt;/b&gt;&lt;br /&gt;&amp;rarr; 모델의 층(layer)을 수직(상하)으로 나누어 각 GPU에 순차적으로 배치&lt;/li&gt;
&lt;li data-end=&quot;2715&quot; data-start=&quot;2648&quot;&gt;&lt;b&gt;텐서 병렬화 (Tensor Parallelism)&lt;/b&gt;&lt;br /&gt;&amp;rarr; 한 층의 연산 자체를 좌우로 나눠 여러 GPU에 병렬 분산시킴&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3) ZeRO(Zero Redundancy Optimizer)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 병렬화를 사용하는 경우 동일한 모델을 여러 GPU에 올리기 때문에 중복으로 모델을 저장하면서 메모리 낭비가 발생합니다. 이런 비효율을 해결하기 위해 ZeRO가 개발됐습니다. 하나의 모델을 하나의 GPU에 올리지 않고 마치 모델 병렬화처럼 모델을 나눠 여러 GPU에 올리고 각 GPU에서는 자신의 모델 부분의 연산만 수행하고 그 상태를 저장하면 메모리를 효율적으로 사용하면서 속도도 빠르게 유지할 수 있다는 것이 ZeRO의 컨셉입니다. Accelerate의 DeepSpeed 사용 가이드를 통해 쉽게 적용할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대규모 언어 모델의 시대에는 단순히 GPU 성능만 믿고 학습을 진행할 수 없습니다. 한정된 자원 안에서 효율적인 학습과 추론을 수행하려면 &lt;b&gt;데이터 타입 최적화, 양자화, 메모리 절감 기법, 분산 학습 전략&lt;/b&gt; 등이 필수적입니다. 적절한 최적화 기법을 조합하면 수천만 원대의 대형 GPU 서버 없이도, 상대적으로 저렴한 환경에서 대형 모델을 실험하고 운영할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;LLM을 활용한 실전 AI 애플리케이션 개발 - 허정준&lt;/span&gt;&lt;/p&gt;</description>
      <category>AI/DeepLearning</category>
      <category>deaplearning</category>
      <category>llm 최적화</category>
      <category>그레이디언트</category>
      <category>메모리 절감</category>
      <category>분산 학습</category>
      <category>양자화</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/88</guid>
      <comments>https://striver.tistory.com/entry/DeepLearning-%EB%A9%94%EB%AA%A8%EB%A6%AC-%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9D%B8-%EB%94%A5%EB%9F%AC%EB%8B%9D#entry88comment</comments>
      <pubDate>Tue, 8 Apr 2025 18:40:17 +0900</pubDate>
    </item>
    <item>
      <title>[LLM 평가] LLM 모델 자체 평가</title>
      <link>https://striver.tistory.com/entry/LLM-evaluation-LLM-%EB%AA%A8%EB%8D%B8-%EC%9E%90%EC%B2%B4-%ED%8F%89%EA%B0%80</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;평소 LLM 애플리케이션을 개발하였지만, 의도대로 답변인 나오는지를 눈대중으로만 확인하다가 신뢰성 확보 및 객관적인 지표를 통한 성능 고도화를 위해 LLM의 성능을 평가해야겠다는 생각이 들었고, 이에 LLM의 성능 평가에 대한 내용을 정리해보고자 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. LLM 평가 배경&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f; text-align: start;&quot;&gt;LLM평가는 모델 자체의 전반적인 성능을 객관적으로 측정하고, 이를 통해 실제 환경에서의 효용성을 예측하는 데 초점을 둡니다. 신뢰할 수 있는 평가 체계를 갖추는 것은 LLM 개발과 응용에서 필수적인 과정입니다. 이러한 LLM 평가는 크게 &lt;span style=&quot;background-color: #ffffff; color: #24292f; text-align: left;&quot;&gt;&lt;b&gt;모델 자체를 평가&lt;/b&gt;하는 것과 &lt;b&gt;LLM 애플리케이션을 평가&lt;/b&gt;하는 두 가지로 구분됩니다. 이번 글에서는 모델 자체 평가에 대해 다뤄보도록 하겠습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) LLM 평가의 주요 접근법&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;LLM 평가는 크게 세 가지 관점에서 이루어집니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;일반적 성능 측정&lt;/b&gt;&lt;br /&gt;- 언어를 얼마나 잘 이해하고 자연스럽게 구사할 수 있는지를 평가합니다.&lt;br /&gt;- 문장의 의미 파악, 번역, 독해, 질문-답변 등 다양한 언어 응용 분야에서의 성능을 측정합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;도메인 특화 성능 측정&lt;/b&gt;&lt;br /&gt;- 법률, 의료, 금융 등 특정 도메인에 대해 파인튜닝된 모델의 성능을 평가합니다.&lt;br /&gt;- 각 분야에 맞게 훈련된 모델이 얼마나 정확한 결과를 도출하는지 측정합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;얼라인먼트(Alignment) 성능 측정&lt;/b&gt;&lt;br /&gt;- 모델이 편향성, 유해성, 윤리적 이슈에 대해 인간의 보편적 기준에 얼마나 부합하는지를 평가합니다.&lt;br /&gt;- 이는 모델이 사회적, 윤리적 기준을 어느 정도 따르는지에 중점을 둡니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) LLM 평가가 어려운 이유&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 설명에 앞서 LLM 평가가 다른 모델에비해 왜 어려운가를 간단하게 설명하고 넘어가겠습니다. 언어 모델의 평가가 어려운 가장 큰 이유는 언어 자체가 지닌 복잡성과 다양성 때문입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;언어의 복잡성&lt;/b&gt;: 언어는 문맥, 뉘앙스, 함축적 의미를 포함하는 복잡한 시스템입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;평가 기준의 다양성&lt;/b&gt;: 단순히 정답을 맞히는 것을 넘어 창의성, 유용성, 적절성 등 다양한 측면에서 평가가 필요합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주관성&lt;/b&gt;: 특히 창의적 텍스트 생성에서는 '좋은' 결과에 대한 정의가 주관적일 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;일관성&lt;/b&gt;: 모델의 성능은 태스크, 도메인, 입력 프롬프트의 형식에 따라 크게 달라질 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 이유로, LLM 평가에서는 다양한 방법론이 병행되어 사용되며, 그중 대표적인 평가 접근법을 아래와 같이 정리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. LLM 정량적 평가&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정량적 평가는 수치적 지표를 통해 모델의 성능을 객관적으로 측정하는 접근법입니다. 주요 평가 지표는 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1474&quot; data-start=&quot;1459&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) Accuracy&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1529&quot; data-start=&quot;1475&quot; data-ke-size=&quot;size16&quot;&gt;전체 예측 중 정답으로 판단된 비율입니다. 정형 데이터에 대한 분류 문제에 가장 널리 사용됩니다.&lt;/p&gt;
&lt;p data-end=&quot;1529&quot; data-start=&quot;1475&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1546&quot; data-start=&quot;1531&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) F1 Score&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1604&quot; data-start=&quot;1547&quot; data-ke-size=&quot;size16&quot;&gt;Precision(정밀도)과 Recall(재현율)의 조화 평균으로, 데이터 불균형 상황에서 유용합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1708&quot; data-start=&quot;1605&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1659&quot; data-start=&quot;1605&quot;&gt;&lt;b&gt;Precision&lt;/b&gt;: 모델이 positive로 예측한 것 중 실제 positive인 비율&lt;/li&gt;
&lt;li data-end=&quot;1708&quot; data-start=&quot;1660&quot;&gt;&lt;b&gt;Recall&lt;/b&gt;: 실제 positive 중 모델이 positive로 예측한 비율&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1755&quot; data-start=&quot;1710&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3) BLEU (Bilingual Evaluation Understudy)&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1834&quot; data-start=&quot;1756&quot; data-ke-size=&quot;size16&quot;&gt;기계 번역 품질을 평가하기 위한 지표로, 생성된 문장이 참조 문장과 얼마나 유사한지 측정합니다. 단어 및 구 단위의 정확도에 중점을 둡니다.&lt;/p&gt;
&lt;p data-end=&quot;1834&quot; data-start=&quot;1756&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1900&quot; data-start=&quot;1836&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;4) ROUGE (Recall-Oriented Understudy for Gisting Evaluation)&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1958&quot; data-start=&quot;1901&quot; data-ke-size=&quot;size16&quot;&gt;주로 문서 요약 성능을 평가하며, 참조 요약과 생성 요약 간의 &lt;b&gt;재현율&lt;/b&gt; 기반 유사도를 측정합니다.&lt;/p&gt;
&lt;p data-end=&quot;1958&quot; data-start=&quot;1901&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1958&quot; data-start=&quot;1901&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;선택 기준 정리&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2074&quot; data-start=&quot;1978&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2030&quot; data-start=&quot;1978&quot;&gt;&lt;b&gt;정형화된 출력&lt;/b&gt; (예: 선택지 문제, 짧은 응답): Accuracy, F1 Score&lt;/li&gt;
&lt;li data-end=&quot;2074&quot; data-start=&quot;2031&quot;&gt;&lt;b&gt;비정형화된 출력&lt;/b&gt; (예: 자연어 요약, 생성): BLEU, ROUGE&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 모델 평가 방식에 따른 분류&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) 확률 기반 평가&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델이 각 후보 답변에 대해 부여한 확률값(log-likelihood)을 기반으로 평가하는 방식입니다. 계산이 빠르고 간단하지만, 실제 생성된 응답과는 차이가 있을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1744012795344&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;질문: 콜라독립 815는 코카콜라에서 만들었나요?
확률 결과: {' 아니오': -3.56..., ' 예': -3.57...}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;아니오&quot;가 더 높은 확률값을 가지므로 정답으로 선택됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) 생성 기반 평가&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확률 기반의 평가는 말그대로 답변에 대한 확률적인 값일 뿐이지, 답변이 정확하게 지문 처럼 출력된 다는 것은 아닙니다. 어떤 경우는 평가에 있어 정확한 워딩이 들어간 결과를 선호 할 수 도 있습니다. 생성 기반 평가는 모델이 실제로 텍스트를 생성하게 한 후, 그 출력을 정답과 비교하는 방식입니다. 이 방법은 실제 사용 시나리오와 더 유사하지만, 평가 시간이 더 오래 걸리고 모델의 출력 형식이 일관되지 않을 수 있다는 단점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1744012871799&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
&quot;doc_id&quot;:10,
&quot;doc&quot;:{
&quot;question&quot;:&quot;밀가루의 제분 후 인공적인 산화를 시키는 첨가물 중 국내에서 사용할 수 없는 것은?&quot;,
&quot;answer&quot;:3,
&quot;A&quot;:&quot;Chlorine Dioxide&quot;,
&quot;B&quot;:&quot;Diluted Benzoyl Peroxide&quot;,
&quot;C&quot;:&quot;Potassium Bromate&quot;,
&quot;D&quot;:&quot;Ammonium Persulfate&quot;,
&quot;Category&quot;:&quot;Food Processing&quot;,
&quot;Human Accuracy&quot;:0.0741
},
&quot;target&quot;:&quot;C&quot;,
[중략]
&quot;resps&quot;:[[&quot;C\n해설：국내에서 사용할 수 없는 것은 Potassium Bromate이다&quot;]],
[중략]
&quot;exact_match&quot;:0.0
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평가 로그인데, 모델의 출력을 보면 답변을 제대로 했지만 뒤에 추가적인 설명이 들어가서 답변이 일치하지 않는다고 판단해서 답변이 틀리다고 판단하였습니다. 이럴때는 맨 앞의 정답만 취하도록 &lt;b&gt;정규식 필터링&lt;/b&gt;을 취해주면 됩니다. 다만 모델의 출력 형식이 일관되지 않으면 정규식을 작성할때 혼란을 겪을 수 있으니 모델의 출력이 일정할때 쓰면 좋은 방법인 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;추가로, 생성 기반의 평가는 직접 추론을 해서 결과를 가지고 평가를 하기 때문에 단순 확률값을 구하는 계산보다 훨씬 더 많은 리소스를 필요로 하게 됩니다. 따라서 생성 기반의 평가가 확률 기반의 평가 보다 평가 시간이 더 소요됩니다.&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;4. 정성적 평가&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정량적 평가만으로는 모델의 모든 측면을 평가하기 어렵습니다. 정성적 평가는 인간의 판단을 통해 모델의 출력 품질을 더 종합적으로 평가하는 접근법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) 인간 평가자 활용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확률 기반의 방법은 모델의 성능을 수치화하는데 유용하지만, 문법적으로 올바른 문장일지라도, 그 의미가 왜곡되거나 맥락에 맞지 않게 나오는 경우가 있기 때문에 생성된 텍스트의 품질을 보장하기 어렵습니다. 또한, 생성 기반의 평가는 생성한 문장을 직접 평가함으로써 더 현실적인 대안으로 보일 수 있지만, 생성된 문장의 품질을 평가하려면 문개 이해, 논리적 일관성, 창의성 등 다양한 요소를 고려해야 하는데, 단순히 필터링 기법이나 데이터 처리로는 평가 방식에 한계가 있습니다. 그렇기에 제일 신뢰 할 수 있는 방법은 사람이 직접 모델의 출력을 검토하는 것입니다. 다음과 같은 측면에서 평가가 이루어질 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;유용성&lt;/b&gt;: 모델의 응답이 사용자의 질문이나 요구를 얼마나 잘 해결했는지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;정확성&lt;/b&gt;: 제공된 정보가 사실과 일치하는지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자연스러움&lt;/b&gt;: 응답이 인간이 작성한 것처럼 자연스러운지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;맥락 이해&lt;/b&gt;: 질문의 맥락을 제대로 파악했는지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;윤리성&lt;/b&gt;: 응답이 윤리적 기준을 준수하는지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 사람이 직접 모델의 출력을 검토한다는 것은 너무 많은 시간과 비용이 소모된다는 단점이 있습니다. 이러한 한계를 해결하기 위해 등장한 것이 LLM as a Judge 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) LLM as a Judge&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에는 다른 LLM을 평가자로 활용하는 접근법이 주목받고 있습니다. 예를 들어, GPT-4와 같은 고성능 모델이 다른 모델의 출력을 평가하는 방식입니다. 이 방법은 인간 평가자를 동원하는 것보다 비용과 시간을 절약할 수 있고, &lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;평가 결과의 신뢰성을 높일 뿐만 아니라, 인간 평가자가 가질 수 있는 주관적인 편향을 줄이는 데도 큰 도움이 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM 평가는 단순한 수치 비교를 넘어 다양한 관점과 방법론이 필요한 복잡한 작업입니다. 정량적 평가는 객관적인 비교를 가능하게 하지만, 언어의 복잡성과 창의성을 완전히 포착하지 못할 수 있습니다. 반면, 정성적 평가는 보다 종합적인 평가를 제공하지만, 주관성과 비용 문제가 있습니다. 최적의 LLM 평가 시스템은 이 두 접근법을 균형 있게 결합하여, 모델의 기술적 성능뿐만 아니라 실제 사용 환경에서의 유용성과 적합성까지 종합적으로 고려해야 합니다. 앞으로 LLM 기술이 발전함에 따라, 이러한 평가 방법론 역시 계속해서 진화할 것으로 기대됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음에는 이러한 평가 방법론을 직접 적용하고 비교해보는 시간을 가져보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://devocean.sk.com/blog/techBoardDetail.do?ID=166716&amp;amp;boardType=techBlog&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://devocean.sk.com/blog/techBoardDetail.do?ID=166716&amp;amp;boardType=techBlog&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://wikidocs.net/238529&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://wikidocs.net/238529&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>LLM Engineering/LLMops</category>
      <category>llm as a judge</category>
      <category>LLM Evaluation</category>
      <category>llm 평가</category>
      <category>정량적 평가</category>
      <category>정성적 평가</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/87</guid>
      <comments>https://striver.tistory.com/entry/LLM-evaluation-LLM-%EB%AA%A8%EB%8D%B8-%EC%9E%90%EC%B2%B4-%ED%8F%89%EA%B0%80#entry87comment</comments>
      <pubDate>Mon, 7 Apr 2025 17:37:07 +0900</pubDate>
    </item>
    <item>
      <title>[LLM 서빙] vLLM vs sglang</title>
      <link>https://striver.tistory.com/entry/LLM-%EC%84%9C%EB%B9%99-vLLM-vs-sglang</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;대규모 언어 모델의 효율적인 서빙을 위해서는 메모리 관리와 병렬 처리 같은 최적화 기술이 필수적입니다. LLM 서빙이란 동일한 연산을 수행하면서도 성능 하락 없이 추론 능력을 향상시키는 방안을 통해 모델 서빙을 수행하는 것을 말합니다. 다양한 프레임워크들이 개발되었지만, 현재 가장 널리 사용되고 자주 언급되는 vLLM과 SGLang에 대해 간단히 설명하고 비교해보는 시간을 가지겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. vLLM&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vLLM은 LLM 추론 및 제공을 위한 빠르고 사용하기 쉬운 라이브러리입니다. UC 버클리의 스카이 컴퓨팅 랩에서 개발되었으며, 학계와 산업계의 기여를 통해 커뮤니티 중심 프로젝트로 발전했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;478&quot; data-origin-height=&quot;255&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kUM3s/btsNaanBJgj/jjJtx4V8hFAVpD9R7KRek0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kUM3s/btsNaanBJgj/jjJtx4V8hFAVpD9R7KRek0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kUM3s/btsNaanBJgj/jjJtx4V8hFAVpD9R7KRek0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkUM3s%2FbtsNaanBJgj%2FjjJtx4V8hFAVpD9R7KRek0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;478&quot; height=&quot;255&quot; data-origin-width=&quot;478&quot; data-origin-height=&quot;255&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;주요 기능&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 고성능 서빙&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PagedAttention 기술을 사용한 메모리 관리와 처리 성능 극대화&lt;/li&gt;
&lt;li&gt;대기중인 요청 연속 처리 가능&lt;/li&gt;
&lt;li&gt;고처리량을 유지하면서도 낮은 지연 시간 실현 가능&lt;/li&gt;
&lt;li&gt;실시간 응용 프로그램에서 강력한 성능을 발휘&lt;/li&gt;
&lt;li&gt;CUDA/HIP 그래프를 통한 빠른 모델 실행&lt;/li&gt;
&lt;li&gt;최적화된 CUDA 커널과 FlashAttention, FlashInfer 통합&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 유연한 배포 및 확장성&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Hugging Face 같은 다양한 모델 허브에서 모델 가져올 수 있음&lt;/li&gt;
&lt;li&gt;Tensor Parallelism, Pipeline Parallelism을 지원하여 여러 GPU에 걸쳐 모델 분산 배포 가능&lt;/li&gt;
&lt;li&gt;대규모 데이터 처리와 추론작업에서 탁월한 성능 제공&lt;/li&gt;
&lt;li&gt;NVIDIA 및 AMD 환경에서 최적화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 다양한 디코딩 알고리즘&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;병렬 샘플링: 다수의 출력 동시 생성&lt;/li&gt;
&lt;li&gt;빔 검색: 최적의 출력 시퀀스 탐색&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) API 호환성&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OpenAI API와 호환되는 서버 설정 가능&lt;/li&gt;
&lt;li&gt;기존 OpenAI API를 사용하던 애플리케이션을 vLLM으로 대체하거나 보완 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5) 최적화 기능&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GPTQ, AWQ, FP8 KV Cache와 같은 다양한 양자화 기법 지원&lt;/li&gt;
&lt;li&gt;모델의 메모리 사용량을 줄이고, GPU에서 실행 시 성능을 최적화&lt;/li&gt;
&lt;li&gt;스트리밍 출력 및 프리픽스 캐싱 지원&lt;/li&gt;
&lt;li&gt;Multi-lora 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6) 하드웨어 지원&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;NVIDIA GPU, AMD CPU 및 GPU&lt;/li&gt;
&lt;li&gt;Intel CPU, Gaudi&amp;reg; 가속기&lt;/li&gt;
&lt;li&gt;IBM Power CPU, TPU&lt;/li&gt;
&lt;li&gt;AWS Trainium 및 Inferentia 가속기&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. sglang&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SGLang은 대규모 언어 모델과 비전 언어 모델을 위한 빠른 서빙 프레임워크입니다. 백엔드 런타임과 프런트엔드 언어를 공동 설계하여 모델과의 상호 작용을 더 빠르고 제어 가능하게 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;141&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkFfti/btsM9Xvbe2K/s9iVcV19XUsnOhwl2gRZK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkFfti/btsM9Xvbe2K/s9iVcV19XUsnOhwl2gRZK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkFfti/btsM9Xvbe2K/s9iVcV19XUsnOhwl2gRZK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkFfti%2FbtsM9Xvbe2K%2Fs9iVcV19XUsnOhwl2gRZK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;546&quot; height=&quot;141&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;141&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;주요 기능&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 빠른 백엔드 런타임&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;접두사 캐싱, 제로 오버헤드 CPU 스케줄러&lt;/li&gt;
&lt;li&gt;연속 배칭, 토큰 어텐션(페이지 어텐션)&lt;/li&gt;
&lt;li&gt;추측 디코딩, 텐서 병렬 처리&lt;/li&gt;
&lt;li&gt;청크별 사전 채우기, 구조화된 출력&lt;/li&gt;
&lt;li&gt;RadixAttention을 통한 효율적인 서비스 제공&lt;/li&gt;
&lt;li&gt;FP8/INT4/AWQ/GPTQ 양자화 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 유연한 프런트엔드 언어&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;체인 생성 호출: 복잡한 생성 작업의 연결&lt;/li&gt;
&lt;li&gt;고급 프롬프팅: 효과적인 프롬프트 전략 지원&lt;/li&gt;
&lt;li&gt;제어 흐름: 조건부 및 반복적 프로세스 구현&lt;/li&gt;
&lt;li&gt;다중 모달 입력: 텍스트와 이미지 등 다양한 입력 처리&lt;/li&gt;
&lt;li&gt;병렬성: 동시 작업 처리&lt;/li&gt;
&lt;li&gt;외부 상호 작용: 다른 시스템과의 통합&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 광범위한 모델 지원&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생성 모델: Llama, Gemma, Mistral, QWen, DeepSeek, LLaVA 등&lt;/li&gt;
&lt;li&gt;임베딩 모델: e5-mistral, gte, mcdse&lt;/li&gt;
&lt;li&gt;보상 모델: Skywork&lt;/li&gt;
&lt;li&gt;새로운 모델을 쉽게 통합할 수 있는 확장성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) 커뮤니티&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오픈 소스로 제공&lt;/li&gt;
&lt;li&gt;활발한 커뮤니티의 지원&lt;/li&gt;
&lt;li&gt;업계에서 점차 채택되는 추세&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. vLLM vs sglang&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 공통점&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;PagedAttention 기술&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;두 프레임워크 모두 메모리 관리 최적화를 위한 PagedAttention 기술 활용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;연속 배칭&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;대기 중인 요청들을 효율적으로 처리하여 처리량 극대화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;양자화 지원&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;GPTQ, AWQ, INT4 등 다양한 양자화 기법 지원으로 모델 경량화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;추측 디코딩&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;생성 속도 향상을 위한 추측 디코딩 메커니즘 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;텐서 병렬 처리&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;대규모 모델의 분산 처리를 위한 텐서 병렬화 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;청크별 사전 채우기&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;대용량 입력을 효율적으로 처리하기 위한 청크 기반 사전 채우기 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) vLLM 강점&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;다양한 하드웨어 지원&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&amp;bull; NVIDIA GPU, AMD CPU/GPU&amp;lt;br&amp;gt;&amp;bull; Intel CPU, IBM Power CPU&amp;lt;br&amp;gt;&amp;bull; TPU, Gaudi 가속기&amp;lt;br&amp;gt;&amp;bull; AWS Trainium/Inferentia&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;OpenAI 호환 API&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;기존 OpenAI API 기반 애플리케이션과의 즉시 호환성 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;다양한 디코딩 알고리즘&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;병렬 샘플링, 빔 검색 등 다양한 디코딩 방식 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;CUDA/HIP 그래프&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;그래프 기반 최적화로 모델 실행 속도 향상&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;멀티로라 지원&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;여러 LoRA 어댑터를 동시에 적용할 수 있는 기능 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) sglang 강점&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;통합 백엔드-프론트엔드 설계&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;백엔드 런타임과 프론트엔드 언어의 공동 설계로 일관된 개발 경험&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;유연한 프로그래밍 모델&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&amp;bull; 체인 생성 호출&amp;lt;br&amp;gt;&amp;bull; 고급 프롬프팅&amp;lt;br&amp;gt;&amp;bull; 제어 흐름&amp;lt;br&amp;gt;&amp;bull; 병렬성 관리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;다중 모달 입력 처리&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;텍스트, 이미지 등 다양한 입력 형식의 통합 처리 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;제로 오버헤드 CPU 스케줄러&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;CPU 리소스의 효율적 활용을 위한 최적화된 스케줄러&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;RadixAttention&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;구조화된 출력 생성을 위한 특화된 어텐션 메커니즘&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;임베딩 및 보상 모델 지원&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&amp;bull; 임베딩 모델: e5-mistral, gte, mcdse&amp;lt;br&amp;gt;&amp;bull; 보상 모델: Skywork&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4. 선택 가이드&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) vLLM 선택 시나리오&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다양한 하드웨어 환경에서 배포 필요&lt;/li&gt;
&lt;li&gt;OpenAI API와의 호환성 중요&lt;/li&gt;
&lt;li&gt;최대한의 처리량과 확장성이 우선시되는 경우&lt;/li&gt;
&lt;li&gt;멀티-LoRA 적용이 필요한 경우&lt;/li&gt;
&lt;li&gt;성숙한 커뮤니티 지원과 광범위한 문서화가 중요한 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) SGLang 선택 시나리오&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;복잡한 생성 작업의 체인화 필요&lt;/li&gt;
&lt;li&gt;고급 프롬프팅 및 제어 흐름 중요&lt;/li&gt;
&lt;li&gt;통합된 백엔드-프론트엔드 접근 방식이 선호되는 경우&lt;/li&gt;
&lt;li&gt;다중 모달 입력 처리 필요&lt;/li&gt;
&lt;li&gt;임베딩 및 보상 모델과의 통합 필요&lt;/li&gt;
&lt;li&gt;구조화된 출력 처리 중요&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vLLM은 다양한 하드웨어 지원과 성숙한 인프라를 갖춘 범용 서빙 솔루션으로, 대규모 배포와 높은 처리량이 필요한 프로젝트에 적합합니다. 반면 SGLang은 프로그래밍 모델과 사용자 경험에 중점을 둔 통합 접근 방식을 제공하여, 복잡한 LLM 애플리케이션 개발에 더 적합할 수 있습니다. 공식문서마다 적절한 예제와 자세한 설명이 제공되고있으니 활용하면 좋을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.vllm.ai/en/latest/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.vllm.ai/en/latest/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.sglang.ai/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.sglang.ai/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://machinelog.tistory.com/entry/vLLM-%EC%9D%B4%EB%9E%80-%EB%AD%98%EA%B9%8C&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://machinelog.tistory.com/entry/vLLM-%EC%9D%B4%EB%9E%80-%EB%AD%98%EA%B9%8C&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>LLM Engineering/LLMops</category>
      <category>llm serve</category>
      <category>llm serveing</category>
      <category>llm 서빙</category>
      <category>serving</category>
      <category>sglnag</category>
      <category>vllm</category>
      <category>서빙</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/86</guid>
      <comments>https://striver.tistory.com/entry/LLM-%EC%84%9C%EB%B9%99-vLLM-vs-sglang#entry86comment</comments>
      <pubDate>Fri, 4 Apr 2025 22:52:05 +0900</pubDate>
    </item>
    <item>
      <title>[Github] 잔디가 심어지지 않는 문제 해결하기</title>
      <link>https://striver.tistory.com/entry/function-calling-vs-tool-calling</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 GitHub와 티스토리 블로그를 연동해서 잔디 심기를 만들어보았고 연동이 잘 된 것을 확인했습니다. 그런데 그 이후 제가 따로 생성한 레포지토리에서 push한 내용이 잔디가 심어지지 않는 현상을 발견했습니다. 오늘은 제가 최근에 겪었던 GitHub 잔디 심기 문제와 그 해결 과정을 공유하려고 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 문제 상황&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 여러 개의 GitHub 레포지토리에서 작업을 하고 있었습니다. 그런데 이상한 점을 발견했습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;레포지토리 A: 코드를 push했더니 GitHub 프로필에 잔디가 잘 심어짐&lt;/li&gt;
&lt;li&gt;레포지토리 B: 코드를 push했는데 GitHub 프로필에 잔디가 심어지지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 레포지토리 설정 문제인가 싶었지만, 둘 다 제가 직접 만든 public 레포지토리였기 때문에 설정 차이는 없었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 원인 분석&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 파악하기 위해 먼저 각 레포지토리의 커밋 로그를 확인해보았습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743830740611&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git log&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 바로 차이점을 발견했습니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잔디가 심어지는 레포지토리의 커밋:&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1743830762143&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;commit f3f623f60ce7f2ba3170694f8c5f320310777841
Author: ehdtjr &amp;lt;dskang207@gmail.com&amp;gt;
Date:   Sat Apr 5 13:42:15 2025 +0900&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잔디가 심어지지 않는 레포지토리의 커밋:&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1743830771472&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;commit e1257241f82e16f8f2ef2143ad335f5b185cb2ae
Author: 동석 &amp;lt;dongseok@dongseog-ui-Macmini.local&amp;gt;
Date:   Sat Apr 5 14:06:07 2025 +0900&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 문제를 파악할 수 있었습니다. 잔디가 심어지지 않는 레포지토리에서는 제 GitHub 계정과 연결된 이메일이 아닌, 컴퓨터의 로컬 사용자 정보를 사용하고 있었던 것입니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 해결 방법&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) 개별 레파지토리 설정 변경&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 잔디가 심어지지 않던 레포지토리에서 git config 설정을 변경했습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743830842237&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git config user.name &quot;ehdtjr&quot;
git config user.email &quot;dskang207@gmail.com&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령어는 현재 레포지토리에만 적용되는 설정입니다. 설정 후 새로운 커밋을 push했더니 드디어 잔디가 심어졌습니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) 전역 설정으로 모든 레포지토리에 적용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 매번 새 레포지토리마다 설정하는 것은 번거롭습니다. 그래서 전역 설정으로 모든 레포지토리에 동일하게 적용했습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1743830883458&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git config --global user.name &quot;ehdtjr&quot;
git config --global user.email &quot;dskang207@gmail.com&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 새로 클론하거나 생성하는 모든 레포지토리에서 GitHub 계정과 연결된 정보로 커밋이 생성되어 잔디가 제대로 심어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4. 느낀점&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC를 바꾼 후부터 레포지토리에 push를 하고 commit log를 살펴보면 아래 이미지처럼 로컬 사용자 정보로 기록이 남았는데, 이것이 GitHub 계정 연결을 하지 않아서 생긴 문제라는 점을 오늘에서야 알게 되었습니다. 평소엔 그냥 &quot;왜 그러지?&quot; 하고 넘어갔는데, 사소한 궁금증도 그냥 넘어가지 말고 이유를 찾아볼 필요가 있다고 다시 한번 생각하게 된 계기가 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;277&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdZjt7/btsNa9haNh2/z6xmNMPw096m6uc6ilDBYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdZjt7/btsNa9haNh2/z6xmNMPw096m6uc6ilDBYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdZjt7/btsNa9haNh2/z6xmNMPw096m6uc6ilDBYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdZjt7%2FbtsNa9haNh2%2Fz6xmNMPw096m6uc6ilDBYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1300&quot; height=&quot;277&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;277&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub 잔디가 심어지지 않는 문제는 대부분 Git 설정의 user.name과 user.email이 GitHub 계정과 연결되지 않아서 발생합니다. 이 설정을 GitHub 계정과 일치시키면 쉽게 해결할 수 있습니다. 최근에 PC를 바꾸면서 기존 설정들이 사라지다보니 이런 문제가 발생했던것 같습니다. 앞으로 새 컴퓨터에서 개발을 시작할 때는 가장 먼저 git config --global 설정을 하는 것을 잊지 말아야겠습니다!&lt;/p&gt;</description>
      <category>Dev/Git&amp;amp;GitHub</category>
      <category>git</category>
      <category>git config</category>
      <category>github</category>
      <category>github config</category>
      <category>github 연동</category>
      <category>github 잔디</category>
      <category>잔디심기</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/85</guid>
      <comments>https://striver.tistory.com/entry/function-calling-vs-tool-calling#entry85comment</comments>
      <pubDate>Thu, 3 Apr 2025 23:17:51 +0900</pubDate>
    </item>
    <item>
      <title>[ESG 투자] ESG 투자란?</title>
      <link>https://striver.tistory.com/entry/LLM-%ED%8F%89%EA%B0%80-%EB%A9%94%ED%8A%B8%EB%A6%AD</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;문서 검색 에이전트를 개발하던 중, 재무제표만을 활용하는 기존 솔루션들과의 차별화 필요성을 느껴 ESG 데이터 활용으로 방향을 전환하게 되었습니다. ESG 및 ESG 투자에 대한 기본 개념부터 실제 데이터 확보 방법까지 정리해보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. ESG 투자란 무엇인가?&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceb1me/btsM9KXOsfj/pvlTdGes1Pirk53KaDvyBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceb1me/btsM9KXOsfj/pvlTdGes1Pirk53KaDvyBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceb1me/btsM9KXOsfj/pvlTdGes1Pirk53KaDvyBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fceb1me%2FbtsM9KXOsfj%2FpvlTdGes1Pirk53KaDvyBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;500&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ESG 투자는 단순한 트렌드가 아닌 현대 투자 전략의 핵심 요소로 자리잡고 있습니다. 환경(Environmental), 사회(Social), 지배구조(Governance) 요소를 고려해 투자 결정을 내리는 이 접근법은 재무적 성과와 사회적 가치를 동시에 추구합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;ESG의 세 가지 핵심 요소&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;환경(E)&lt;/b&gt;: 탄소 배출량, 에너지 효율성, 폐기물 관리, 자원 사용, 기후변화 대응 등 기업의 환경 영향을 평가&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사회(S)&lt;/b&gt;: 인권, 노동 조건, 다양성과 포용성, 공정한 보상, 제품 안전성 등 기업과 이해관계자 간 관계를 평가&lt;/li&gt;
&lt;li&gt;&lt;b&gt;지배구조(G)&lt;/b&gt;: 이사회 구성, 경영진 보상, 회계 투명성, 윤리적 비즈니스 관행, 주주 권리 등 기업 운영 방식을 평가&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. ESG 투자 지표와 평가&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ESG 투자에는 신뢰할 수 있는 데이터와 표준화된 지표가 필수적입니다. MSCI, Sustainalytics, Bloomberg, S&amp;amp;P Global 등의 기관이 주요 평가 지표를 개발하여 제공하고 있습니다.&lt;span style=&quot;color: #000000; font-size: 1.44em; letter-spacing: -1px; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;주요 ESG 평가 지표&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;환경 지표&lt;/b&gt;: 탄소 발자국, 에너지 효율성, 수자원 사용량, 폐기물 관리, 환경 규제 준수&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사회 지표&lt;/b&gt;: 직원 다양성, 작업장 안전, 인권 정책, 공급망 관리, 지역사회 참여&lt;/li&gt;
&lt;li&gt;&lt;b&gt;지배구조 지표&lt;/b&gt;: 이사회 독립성, 경영진 보상 구조, 부패 방지 정책, 세금 투명성, 주주 권리&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. ESG 투자의 성공 사례&lt;/b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;ESG 원칙을 효과적으로 적용한 기업들의 성공 사례는 ESG 투자의 가치를 입증합니다:&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;유니레버&lt;/b&gt;: '지속가능한 생활 계획'을 통해 환경 영향을 줄이고 사회적 가치를 창출하면서도 재무적 성과 달성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;테슬라&lt;/b&gt;: 전기차와 청정 에너지 솔루션으로 환경 영향 감소에 기여하며 기업 가치 증대&lt;/li&gt;
&lt;li&gt;&lt;b&gt;파타고니아&lt;/b&gt;: 환경 보호 중심의 기업 문화와 지속가능한 제품으로 소비자 신뢰와 브랜드 가치 제고&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4.ESG 투자의 위험 요소와 관리&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ESG 투자에도 주의해야 할 위험 요소가 존재합니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;그린워싱&lt;/b&gt;: 기업이 실제 ESG 개선 노력은 미미하면서 성과를 과장하는 문제&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 신뢰성&lt;/b&gt;: 표준화가 미흡하고 평가 기관마다 다른 방법론을 사용하는 문제&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단기 성과 압박&lt;/b&gt;: 장기적 관점이 필요한 ESG 투자와 단기 수익 추구 간의 갈등&lt;/li&gt;
&lt;li&gt;&lt;b&gt;지역적 차이&lt;/b&gt;: 글로벌 투자에서 지역마다 다른 ESG 기준과 규제&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;5. ESG 투자를 선택해야 하는 이유&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ESG 투자는 다양한 측면에서 매력적인 선택입니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;재무적 성과 개선&lt;/b&gt;: 연구에 따르면 ESG 성과가 우수한 기업이 장기적으로 더 나은 재무 성과를 보이는 경향&lt;/li&gt;
&lt;li&gt;&lt;b&gt;위험 관리&lt;/b&gt;: 환경 규제, 소송, 평판 손상 등 잠재적 위험을 선제적으로 관리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장기적 가치 창출&lt;/b&gt;: 지속가능한 비즈니스 모델을 통한 안정적 성장과 가치 창출&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사회적 영향&lt;/b&gt;: 환경 보호, 사회 정의, 기업 책임 등 긍정적인 사회적 변화에 기여&lt;/li&gt;
&lt;li&gt;&lt;b&gt;투자자 수요 증가&lt;/b&gt;: 특히 밀레니얼과 Z세대의 가치 기반 투자 선호도 상승&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;6. ESG 데이터 확보 방법&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ESG 데이터를 확보하기 위해 다양한 방법을 검토한 결과, 많은 데이터 제공 서비스가 유료이거나 별도의 승인이 필요했습니다. MSCI, Sustainalytics, Bloomberg, S&amp;amp;P Global 등은 대부분 유료 서비스를 제공합니다. 그러나 Yahoo Finance에서 무료로 기본적인 ESG 데이터를 확인할 수 있었습니다. 종목 검색 후 Sustainability 항목에서 ESG 종합 점수와 환경(E), 사회(S), 구조(G) 각각의 점수를 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1575&quot; data-origin-height=&quot;929&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pEwz1/btsNbAFG6vr/ukh77psbmBSdc3WbS9ZYd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pEwz1/btsNbAFG6vr/ukh77psbmBSdc3WbS9ZYd1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pEwz1/btsNbAFG6vr/ukh77psbmBSdc3WbS9ZYd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpEwz1%2FbtsNbAFG6vr%2Fukh77psbmBSdc3WbS9ZYd1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1575&quot; height=&quot;929&quot; data-origin-width=&quot;1575&quot; data-origin-height=&quot;929&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 종목에 해당하는건 아니지만 종목을 검색하고 Sustainability 항목에 들어가면 하단 이미지처럼 ESG 점수와 각 E,S,G에 대한 점수가 매겨져있습니다. 다른 ESG 요소에 대한 데이터를 추가적으로 가져오고 싶지만 yahhoo Finance에서 지원하는 API에서 무료로 가져올 수 있는 데이터에는 한계가 있는 것 같았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1234&quot; data-origin-height=&quot;866&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqFtkZ/btsNalCZ773/ncYJXkui7HdE9fZweSxWck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqFtkZ/btsNalCZ773/ncYJXkui7HdE9fZweSxWck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqFtkZ/btsNalCZ773/ncYJXkui7HdE9fZweSxWck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqFtkZ%2FbtsNalCZ773%2FncYJXkui7HdE9fZweSxWck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1234&quot; height=&quot;866&quot; data-origin-width=&quot;1234&quot; data-origin-height=&quot;866&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yfinance 라이브러리를 사용해 ESG 데이터를 쉽게 가져올 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743922495225&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import yfinance as yf
ticker = yf.Ticker(&quot;NVDA&quot;)
esg_data = ticker.sustainability
print(esg_data)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드를 실행하면 NVIDIA의 ESG 점수 데이터를 확인할 수 있습니다. 현재는 기본적인 점수 데이터만 확보 가능하지만, 향후 추가 데이터 소스를 발굴하거나 유료 서비스를 검토하여 더 풍부한 ESG 데이터를 확보할 계획입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;821&quot; data-origin-height=&quot;507&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b72y9r/btsNborNAjH/VRPVB3dvqJpSCZKmHSNNd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b72y9r/btsNborNAjH/VRPVB3dvqJpSCZKmHSNNd1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b72y9r/btsNborNAjH/VRPVB3dvqJpSCZKmHSNNd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb72y9r%2FbtsNborNAjH%2FVRPVB3dvqJpSCZKmHSNNd1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;821&quot; height=&quot;507&quot; data-origin-width=&quot;821&quot; data-origin-height=&quot;507&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://creativestudio.kr/3542&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://creativestudio.kr/3542&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.samsungsds.com/kr/insights/esg-global-trends-in-2025.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.samsungsds.com/kr/insights/esg-global-trends-in-2025.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.hankyung.com/article/202412201003i&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.hankyung.com/article/202412201003i&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.fidelity.co.kr/insight-and-learning/learn-about-investing/esg-investing/esg-benefits&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.fidelity.co.kr/insight-and-learning/learn-about-investing/esg-investing/esg-benefits&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Activities/FinAgent Lab</category>
      <category>ESG</category>
      <category>esg 투자</category>
      <category>Yahoo Finance</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/84</guid>
      <comments>https://striver.tistory.com/entry/LLM-%ED%8F%89%EA%B0%80-%EB%A9%94%ED%8A%B8%EB%A6%AD#entry84comment</comments>
      <pubDate>Wed, 2 Apr 2025 23:10:28 +0900</pubDate>
    </item>
    <item>
      <title>MCP Client &amp;amp; Server 직접 구현하기</title>
      <link>https://striver.tistory.com/entry/MCP-Client-Server-%EC%A7%81%EC%A0%91-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;MCP의 원리와 Claude를 이용한 사용방법을 알아보았지만, 더 근본적으로 MCP Client와 Server가 어떻게 만들지고 사용되는지에 대해 더 깊게이해하고 넘어가야 추후에 응용하는데 도움이 될 것 같아. 유튜브에서 MCP Client, Server를 직접 만들어서 Youtube Agent 데모를 구현한 예제가 있길래, 이걸 보면서 Mcp Client와 Server의 생성방법과 작동 원리에 대해 이해하고 넘어가도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. MCP 서버 생성&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 MCP 서버를 만들어줍니다. Server라고 생각하면 어렵게 느껴질 수 있는데, MCP 서버는 생각보다 단순합니다. 일반 함수를 작성하는 것과 크게 다르지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743694142497&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from mcp.server.fastmcp import FastMCP

# Create an MCP server
mcp = FastMCP(&quot;youtube_agent_server&quot;)

@mcp.tool()
def get_youtube_transcript(url: str) -&amp;gt; str:
    &quot;&quot;&quot; 유튜브 영상 URL에 대한 자막을 가져옵니다.&quot;&quot;&quot;
    
    # 1. 유튜브 URL에서 비디오 ID를 추출합니다.
    video_id_match = re.search(r&quot;(?:v=|\/)([0-9A-Za-z_-]{11}).*&quot;, url)
    if not video_id_match:
        raise ValueError(&quot;유효하지 않은 YouTube URL이 제공되었습니다&quot;)
    video_id = video_id_match.group(1)
    
    languages = [&quot;ko&quot;, &quot;en&quot;]
    # 2. youtube_transcript_api를 사용하여 자막을 가져옵니다.
    try:
        transcript_list = YouTubeTranscriptApi.get_transcript(video_id, languages=languages)
        
        # 3. 자막 목록의 'text' 부분을 하나의 문자열로 결합합니다.
        transcript_text = &quot; &quot;.join([entry[&quot;text&quot;] for entry in transcript_list])
        return transcript_text

    except Exception as e:
        raise RuntimeError(f&quot;비디오 ID '{video_id}'에 대한 자막을 찾을 수 없거나 사용할 수 없습니다.{e}&quot;)
        
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 앞서 MCP Server를 &quot;표준화된 모델 컨텍스트 프로토콜을 활용해 특정 기능을 노출하는 경량 프로그램&quot;이라고 정의하였습니다. 그럼 우리가 MCP Server에서 구현해야할 것은 &quot;특정 기능&quot;이는 것입니다. 이 특정 기능을 함수화 해서 만들어두고 &quot;@mcp.tool()&quot; 데코레이터로 표시해두면 됩니다. 이 데코레이터는 함수자체가 mcp client들이 활용 가능한 형태로 자동으로 변환 시켜주는 역할을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 포인트&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tool Calling을 통한 Agent를 구현해 보신 분들은 익숙하겠지만 Agent에게 도구를 여러개 Mapping 시켜줬을때 Agent는 어떤 상황에 어떤 도구를 써야할지 판단을 내리기 힘듭니다. 그래서 도구마다 description을 자세히 적어줄 필요가 있습니다. MCP도 마찬가지입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;상세한 설명(docstring)&lt;/b&gt;: LLM이 도구의 목적과 사용법을 정확히 이해하도록 돕습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;타입 힌트&lt;/b&gt;: 인자와 반환값의 타입을 명시하여 LLM이 더 정확하게 도구를 사용할 수 있게 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743694641680&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if __name__ == &quot;__main__&quot;:
    print(&quot;Starting MCP server...&quot;)
    mcp.run()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만들고 싶은 도구를 이어서 만들어주고 하단에 mcp.run()으로 mcp server 코드를 마무리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. MCP Clinet 연동을 위한 준비&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP Clinet를 생성하기에 앞서 &lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;로컬 MCP 서버를 연동하려면, &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;서버 실행에 필요한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;Python 실행 파일 경로&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;MCP 서버 스크립트 경로&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;를 JSON 설정에 입력해야 합니다. 따라서 별도의 json파일을 만들고 하단 Json 파일처럼 작성해줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743695117410&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;mcpServers&quot;: {
    &quot;mcp-test&quot;: {
      &quot;command&quot;: &quot;/Users/yourname/projects/python_mcp_agent/venv/bin/python&quot;,
      &quot;args&quot;: [
        &quot;/Users/yourname/projects/python_mcp_agent/2_mcp_server.py&quot;
      ]
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;command에 가상환경의 경로, args에 우리가 생성한 mcp Server의 경로를 작성해줍니다. 경로는 왼쪽 디렉토리 구조에서 우클릭으로 Path를 확인하는게 제일 간편합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. MCP Client 생성&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 본격적인 준비가 끝났으니 MCP Clinet를 생성하고 Server와 연동해서 실행해보겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743695453693&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from agents.mcp import MCPServerStdio

# MCP 서버 설정
async def setup_mcp_servers():
    servers = []
    
    # mcp.json 파일에서 설정 읽기
    with open('mcp.json', 'r') as f:
        config = json.load(f)
    
    # 구성된 MCP 서버들을 순회
    for server_name, server_config in config.get('mcpServers', {}).items():
        mcp_server = MCPServerStdio(
            params={
                &quot;command&quot;: server_config.get(&quot;command&quot;),
                &quot;args&quot;: server_config.get(&quot;args&quot;, [])
            },
            cache_tools_list=True
        )
        await mcp_server.connect()
        servers.append(mcp_server)

    return servers&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 작성한 json 파일을 가져와서 서버 목록을 확인하고 순회하며 연결합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743695535295&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from agents import Agent, Runner

# 에이전트 설정
async def setup_agent():
    # 서버가 이미 존재하는지 확인하고, 없으면 생성
    mcp_servers = await setup_mcp_servers()
    
    agent = Agent(
        name=&quot;Assistant&quot;,
        instructions=&quot;너는 유튜브 컨텐츠 분석을 도와주는 에이전트야&quot;,
        model=&quot;gpt-4o-mini&quot;,
        mcp_servers=mcp_servers
    )
    return agent,mcp_servers&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 사용한 Agent는 OpenaAI Agents SDK로 Openai에서 정식으로 공개한 패키지입니다. Agent를 간단하게 만들고 MCP Server 연동도 쉽게 할 수 있도록 도와줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743695713042&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 메시지 처리
async def process_user_message():
    agent,mcp_servers = await setup_agent()
    messages = st.session_state.chat_history

    result = Runner.run_streamed(agent, input=messages)

    response_text = &quot;&quot;
    placeholder = st.empty()

    async for event in result.stream_events():
        # LLM 응답 토큰 스트리밍
        if event.type == &quot;raw_response_event&quot; and isinstance(event.data, ResponseTextDeltaEvent):
            response_text += event.data.delta or &quot;&quot;
            with placeholder.container():
                with st.chat_message(&quot;assistant&quot;):
                    st.markdown(response_text)


        # 도구 이벤트와 메시지 완료 처리
        elif event.type == &quot;run_item_stream_event&quot;:
            item = event.item

            if item.type == &quot;tool_call_item&quot;:
                tool_name = item.raw_item.name
                st.toast(f&quot;  도구 활용: `{tool_name}`&quot;)


    st.session_state.chat_history.append({
        &quot;role&quot;: &quot;assistant&quot;,
        &quot;content&quot;: response_text
    })
    # 명시적 종료 (streamlit에서 비동기 처리 오류 방지)
    for server in mcp_servers:
        await server.__aexit__(None, None, None)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;streamlit 기본 설정을 마친뒤 위 코드로 사용자의 입력에 대해 &quot;Runner.run_streamed(agent, input=messages)&quot; 실행하고 결과값을 받아와 화면에 보여주면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만들어져있는 MCP Server를 가져다가 사용하는 것도 좋지만, 어떤 원리로 작동하는지를 코드를 뜯어보면서 살펴보니 확실히 이해하는데 더 도움이 되는 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Rn5HMaWunx4&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=Rn5HMaWunx4&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/dabidstudio/python_mcp_agent&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/dabidstudio/python_mcp_agent&lt;/a&gt;&lt;/p&gt;</description>
      <category>MCP</category>
      <category>mcp client</category>
      <category>mcp server</category>
      <category>mcp 직접</category>
      <category>model context protocol</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/83</guid>
      <comments>https://striver.tistory.com/entry/MCP-Client-Server-%EC%A7%81%EC%A0%91-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0#entry83comment</comments>
      <pubDate>Tue, 1 Apr 2025 23:08:14 +0900</pubDate>
    </item>
    <item>
      <title>[MCP] Github MCP Server 사용하기</title>
      <link>https://striver.tistory.com/entry/MCP-Github-MCP-Server-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전 포스트에서 Claude에서 MCP를 사용하는 모습을 간단히 소개해드렸는데요, 이번에는 한 단계 더 나아가 직접 MCP 서버를 가져다가 사용해보도록 하겠습니다. 직접 개발하는 대신 이미 만들어진 GitHub MCP 서버를 활용하여 Claude와 연결하는 방법을 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. Claude에서 MCP 사용 준비하기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude로 MCP를 사용하기 위해서는 두 가지가 필요합니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Claude Pro 구독 결제&lt;/li&gt;
&lt;li&gt;Claude 데스크톱 앱 설치&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude 데스크톱 앱은 공식 웹사이트 좌측 하단에서 다운로드하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1618&quot; data-origin-height=&quot;1231&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdKkYR/btsM1MgvnB7/w1rjMgUp0tjpTJNl6LYlTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdKkYR/btsM1MgvnB7/w1rjMgUp0tjpTJNl6LYlTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdKkYR/btsM1MgvnB7/w1rjMgUp0tjpTJNl6LYlTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdKkYR%2FbtsM1MgvnB7%2Fw1rjMgUp0tjpTJNl6LYlTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1618&quot; height=&quot;1231&quot; data-origin-width=&quot;1618&quot; data-origin-height=&quot;1231&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 claude 앱을 다운로드 받고나면 설정-&amp;gt; 개발자 탭을 들어가줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1012&quot; data-origin-height=&quot;759&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceejB8/btsM1NUniqA/WEYiSpTbHtegtdpj7MBNV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceejB8/btsM1NUniqA/WEYiSpTbHtegtdpj7MBNV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceejB8/btsM1NUniqA/WEYiSpTbHtegtdpj7MBNV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceejB8%2FbtsM1NUniqA%2FWEYiSpTbHtegtdpj7MBNV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1012&quot; height=&quot;759&quot; data-origin-width=&quot;1012&quot; data-origin-height=&quot;759&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;윈도우의 경우 좌측 상단에 설정탭이 따로 있을 것이고, mac os의 경우 mac 화면 자체 좌측 상단에 보시면 claude 설정 탭이 있으니 그걸 사용하시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;483&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DUOrc/btsM1OFN3zV/Fxw9BSkD3SgiKo1ugEo8D1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DUOrc/btsM1OFN3zV/Fxw9BSkD3SgiKo1ugEo8D1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DUOrc/btsM1OFN3zV/Fxw9BSkD3SgiKo1ugEo8D1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDUOrc%2FbtsM1OFN3zV%2FFxw9BSkD3SgiKo1ugEo8D1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;880&quot; height=&quot;483&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;483&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. MCP 설정하기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 개발자 탭을 들어가면 아무것도 뜨지않는데, 나중에 도구를 추가하면 우리가 추가한 도구의 리스트들이 이 화면에서 보여지게됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1005&quot; data-origin-height=&quot;758&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beGuaT/btsM31DqVng/OtfDgoBsXRjC5XAYrM0KK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beGuaT/btsM31DqVng/OtfDgoBsXRjC5XAYrM0KK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beGuaT/btsM31DqVng/OtfDgoBsXRjC5XAYrM0KK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeGuaT%2FbtsM31DqVng%2FOtfDgoBsXRjC5XAYrM0KK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1005&quot; height=&quot;758&quot; data-origin-width=&quot;1005&quot; data-origin-height=&quot;758&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &quot;시작하기&quot;를 누르면 MCP 설정에 도움을 주는 공식문서로 넘어가고, &quot;설정 편집&quot;을 누르시면 claude가 설치되어있는 폴더로 들어오게 됩니다. 이걸 원하는 에디터로 열어줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;395&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ApDMC/btsM4heYdjx/uQyCNKxRxzcFpW3Dcrli8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ApDMC/btsM4heYdjx/uQyCNKxRxzcFpW3Dcrli8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ApDMC/btsM4heYdjx/uQyCNKxRxzcFpW3Dcrli8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FApDMC%2FbtsM4heYdjx%2FuQyCNKxRxzcFpW3Dcrli8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;678&quot; height=&quot;395&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;395&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 들어오게 되면 저처럼 아무것도 없는 비어있는 Json 코드가 보이실 겁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;345&quot; data-origin-height=&quot;278&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0qOYU/btsM17Y4oog/M773kci1Ol9F7XvAmaqip0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0qOYU/btsM17Y4oog/M773kci1Ol9F7XvAmaqip0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0qOYU/btsM17Y4oog/M773kci1Ol9F7XvAmaqip0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0qOYU%2FbtsM17Y4oog%2FM773kci1Ol9F7XvAmaqip0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;345&quot; height=&quot;278&quot; data-origin-width=&quot;345&quot; data-origin-height=&quot;278&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 저희가 여러가지 MCP 들을 여기에 넣어주고 저장해주면 Claude에서 사용이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. Github MCP 서버 연결하기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 실제로 MCP를 추가해보겠습니다. 저는 Smithery에서 제공하는 Github MCP 서버를 사용하여 제 리포지토리의 README.md 내용을 작성해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1274&quot; data-origin-height=&quot;891&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WSXap/btsM15NGbzj/81BtlKgi9dHToGaVe7eG8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WSXap/btsM15NGbzj/81BtlKgi9dHToGaVe7eG8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WSXap/btsM15NGbzj/81BtlKgi9dHToGaVe7eG8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWSXap%2FbtsM15NGbzj%2F81BtlKgi9dHToGaVe7eG8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1274&quot; height=&quot;891&quot; data-origin-width=&quot;1274&quot; data-origin-height=&quot;891&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;github login을 해주고 Access token을 입력해줍니다. Token은 Github -&amp;gt; Settings -&amp;gt; 좌측 하단의 Developer settings -&amp;gt; Personal access tokens -&amp;gt; Fine-grained tokens 에서 발급받아주시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1358&quot; data-origin-height=&quot;856&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJkVpn/btsM3lJe7zt/a7yY45fkPoRQxGoA6GLikK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJkVpn/btsM3lJe7zt/a7yY45fkPoRQxGoA6GLikK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJkVpn/btsM3lJe7zt/a7yY45fkPoRQxGoA6GLikK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJkVpn%2FbtsM3lJe7zt%2Fa7yY45fkPoRQxGoA6GLikK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1358&quot; height=&quot;856&quot; data-origin-width=&quot;1358&quot; data-origin-height=&quot;856&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한가지 주의할점은 &quot;command&quot; : &quot;npx&quot;로 되어있는데 우리가 사용하려는 Pc에 node.js가 설치되어있어야지만 이 명령어를 사용할 수 있다는 의미이기 때문에 pc에 node.js가 설치되어있지 않은분들은 설치 후에 진행해주시면 되겠습니다. 그리고 json 파일을 복사해서 &quot;claude_desktop_config.json&quot;에 붙혀넣어줍니다. 그 후 Calude 앱을 껐다가 다시 실행해주면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;781&quot; data-origin-height=&quot;550&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lQsoC/btsM2WvYQ49/bP5mXBLPGKGQzHWzAkGhik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lQsoC/btsM2WvYQ49/bP5mXBLPGKGQzHWzAkGhik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lQsoC/btsM2WvYQ49/bP5mXBLPGKGQzHWzAkGhik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlQsoC%2FbtsM2WvYQ49%2FbP5mXBLPGKGQzHWzAkGhik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;781&quot; height=&quot;550&quot; data-origin-width=&quot;781&quot; data-origin-height=&quot;550&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP Server가 성공적으로 연결된 모습을 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4. MCP 서버 사용해보기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;질문을 하면 MCP Server를 연결한 도구를 사용하려고할때 사용해도 될지 물어보는데 이때 허용을 눌러주면 도구를 사용해서 답변을 생성하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1251&quot; data-origin-height=&quot;901&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dh6FDB/btsM2Ln3ojJ/7Sk29K32wbasJX5xf5X6Zk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dh6FDB/btsM2Ln3ojJ/7Sk29K32wbasJX5xf5X6Zk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dh6FDB/btsM2Ln3ojJ/7Sk29K32wbasJX5xf5X6Zk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdh6FDB%2FbtsM2Ln3ojJ%2F7Sk29K32wbasJX5xf5X6Zk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1251&quot; height=&quot;901&quot; data-origin-width=&quot;1251&quot; data-origin-height=&quot;901&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude가 생성한 답변이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;756&quot; data-origin-height=&quot;352&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFFwpb/btsM2xcd0Pf/9lmzrLlsermGwSlbnBbWw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFFwpb/btsM2xcd0Pf/9lmzrLlsermGwSlbnBbWw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFFwpb/btsM2xcd0Pf/9lmzrLlsermGwSlbnBbWw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFFwpb%2FbtsM2xcd0Pf%2F9lmzrLlsermGwSlbnBbWw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;756&quot; height=&quot;352&quot; data-origin-width=&quot;756&quot; data-origin-height=&quot;352&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제 Repositorys에 대한 내용입니다. 틀린 내용없이 잘 가져온 모습을 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;924&quot; data-origin-height=&quot;599&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bogRcX/btsM4kbLgfB/f2226V6iGBvN1Xck09Nvm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bogRcX/btsM4kbLgfB/f2226V6iGBvN1Xck09Nvm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bogRcX/btsM4kbLgfB/f2226V6iGBvN1Xck09Nvm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbogRcX%2FbtsM4kbLgfB%2Ff2226V6iGBvN1Xck09Nvm1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;924&quot; height=&quot;599&quot; data-origin-width=&quot;924&quot; data-origin-height=&quot;599&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 생각했던 md 파일을 만들어달라고 해보니 생각보다 잘 만들어주는 모습입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;697&quot; data-origin-height=&quot;660&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HzHuD/btsM37XUi8S/WaZKceLN4ywo6ODhWqpFm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HzHuD/btsM37XUi8S/WaZKceLN4ywo6ODhWqpFm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HzHuD/btsM37XUi8S/WaZKceLN4ywo6ODhWqpFm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHzHuD%2FbtsM37XUi8S%2FWaZKceLN4ywo6ODhWqpFm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;697&quot; height=&quot;660&quot; data-origin-width=&quot;697&quot; data-origin-height=&quot;660&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP를 사용해보니 꽤 유용한 기능이지만, 질문에 따라 가끔 MCP 서버 오류가 발생하기도 합니다. 이는 질문을 얼마나 정확하게, 어떤 방식으로 하는지가 중요한 요소로 작용함을 의미합니다. 현재 단계에서는 완전히 안정적이라고 느끼지는 않았지만, 유용성과 발전 가능성이 충분히 있다고 생각됩니다. 앞으로 더 깊이 탐구해볼 가치가 있는 기술이라고 판단됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=0sUN3d4atoc&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=0sUN3d4atoc&lt;/a&gt;&lt;/p&gt;</description>
      <category>Claude</category>
      <category>claude mcp</category>
      <category>github</category>
      <category>github mcp server</category>
      <category>MCP</category>
      <category>mcp server</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/82</guid>
      <comments>https://striver.tistory.com/entry/MCP-Github-MCP-Server-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0#entry82comment</comments>
      <pubDate>Mon, 31 Mar 2025 15:53:57 +0900</pubDate>
    </item>
    <item>
      <title>[LLMops] Opik으로 LLM 평가하기</title>
      <link>https://striver.tistory.com/entry/LLMops-Opik%EC%9C%BC%EB%A1%9C-LLM-%ED%8F%89%EA%B0%80%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전 포스팅에서 소개한 Opik 프레임워크를 활용하여 실제 LLM 응답을 평가하는 방법에 대해 알아보겠습니다. Opik은 다양한 메트릭을 통해 LLM 출력물의 품질을 체계적으로 평가할 수 있는 프레임워크를 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. Opik 평가의 주요 구성 요소&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Opik에서 평가를 진행하기 위해서는 다음 세 가지 핵심 구성요소가 필요합니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 데이터 세트&lt;br /&gt;-&amp;nbsp;LLM&amp;nbsp;응용&amp;nbsp;프로그램에&amp;nbsp;대한&amp;nbsp;입력과&amp;nbsp;선택적으로&amp;nbsp;예상&amp;nbsp;출력을&amp;nbsp;나타내는&amp;nbsp;샘플의&amp;nbsp;컬렉션&lt;br /&gt;- 각 샘플에 대한 입력 및 예상 출력만 저장하며, LLM 지원서의 출력은 평가 프로세스 중에 계산되고 점수가 매겨집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;2) 평가 과제&lt;br /&gt;-&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;프롬프트&amp;nbsp;템플릿&amp;nbsp;또는&amp;nbsp;빌드&amp;nbsp;중인&amp;nbsp;LLM&amp;nbsp;애플리케이션입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;3) 메트릭&lt;br /&gt;-&amp;nbsp;LLM&amp;nbsp;결과물을&amp;nbsp;채점할&amp;nbsp;때&amp;nbsp;사용하려는&amp;nbsp;메트릭&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 데이터 세트 생성 방법&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평가 과제는 직접 만든 LLM 애플리 케이션을 사용하면 되고 메트릭은 이후에 자세히 설명하겠지만 Opik에서 기본적으로 제공해주는 메트릭의 종류가 비교적 다양하기 때문에 이걸 사용해도 됩니다. 그렇다면 문제는 데이터 세트입니다. LLM을 평가하기 위해 필수적으로 요구되는 데이터세트에 대해 Opik은 3가지 생성 방법을 말해주고있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Python SDK&lt;/b&gt;: Python SDK를 사용하여 데이터 세트를 만들고 여기에 항목을 추가할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;추적 테이블&lt;/b&gt;: 기존에 기록된 추적 내용(예: 프로덕션 애플리케이션의 추적 내용)을 데이터 세트에 추가할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Opik UI&lt;/b&gt;: 수동으로 데이터 세트를 만들고 항목을 추가할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SDK를 사용해 데이터 세트를 생성하는 방법에 대해 샘플코드를 살펴보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743149891734&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from opik import Opik

# Get or create a dataset
client = Opik()
dataset = client.get_or_create_dataset(name=&quot;My dataset&quot;)

# Add dataset items to it
dataset.insert([
    {&quot;user_question&quot;: &quot;Hello, world!&quot;, &quot;expected_output&quot;: {&quot;assistant_answer&quot;: &quot;Hello, world!&quot;}},
    {&quot;user_question&quot;: &quot;What is the capital of France?&quot;, &quot;expected_output&quot;: {&quot;assistant_answer&quot;: &quot;Paris&quot;}},
])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;SDK 활용 시 주요 특징&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;자동 중복 제거&lt;/b&gt;: 동일한 항목을 여러 번 삽입해도 데이터 세트에는 한 번만 저장됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;get_or_create_dataset()&lt;/b&gt;: 지정된 이름의 데이터 세트가 있으면 가져오고, 없으면 새로 생성합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;다양한 형식 지원&lt;/b&gt;: JSONL, Pandas 데이터프레임 등 다양한 형식으로 데이터를 추가할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. Online Evaluation 설정 방법&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 개발하고 있는 에이전트에 평가를 적용하기 위해서는 어떤 방법을 사용하던 retrieval에 사용한 데이터를 기반으로 별도의 dataset을 새롭게 만들어줘야한다는 사실은 변함이 없었습니다. 그래서 dataset 구축에 대해 더 알아볼까하던참에 Opik에서 제공하는 Online Evaluation을 발견했습니다. Online Evaluation 기능을 활용하면 프로덕션 환경에서 실시간으로 LLM을 평가 할 수 있습니다. 이 기능은 별도의 데이터 세트 구축 없이도 실시간 평가가 가능한 장점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 project를 지정하고 &lt;b&gt;online evaluation Tab&lt;/b&gt;에 들어가서&lt;b&gt; Create new rule&lt;/b&gt;를 클릭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2305&quot; data-origin-height=&quot;1227&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/diRxfc/btsMZ4A8QNi/8Dq9sC39AQuuVFl2mmPKnk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/diRxfc/btsMZ4A8QNi/8Dq9sC39AQuuVFl2mmPKnk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/diRxfc/btsMZ4A8QNi/8Dq9sC39AQuuVFl2mmPKnk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdiRxfc%2FbtsMZ4A8QNi%2F8Dq9sC39AQuuVFl2mmPKnk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2305&quot; height=&quot;1227&quot; data-origin-width=&quot;2305&quot; data-origin-height=&quot;1227&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평가 규칙에 필요한 정보를 입력해주겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;776&quot; data-origin-height=&quot;1060&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l0oXT/btsMZfJK6hv/bXGR0DIgUZuKbUihlnk5yK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l0oXT/btsMZfJK6hv/bXGR0DIgUZuKbUihlnk5yK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l0oXT/btsMZfJK6hv/bXGR0DIgUZuKbUihlnk5yK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl0oXT%2FbtsMZfJK6hv%2FbXGR0DIgUZuKbUihlnk5yK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;776&quot; height=&quot;1060&quot; data-origin-width=&quot;776&quot; data-origin-height=&quot;1060&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직까지 제공하는 Model은 OpenAI, Anthropic, OpenRouter, Ollama, Gemini 처럼 자주 언급되고 활용되는 Modle 위주로 사용이 가능한 것 같았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;1058&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8c0gH/btsMYPdVJlt/MAMYhDzA3GYIgCFQu1D0m1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8c0gH/btsMYPdVJlt/MAMYhDzA3GYIgCFQu1D0m1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8c0gH/btsMYPdVJlt/MAMYhDzA3GYIgCFQu1D0m1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8c0gH%2FbtsMYPdVJlt%2FMAMYhDzA3GYIgCFQu1D0m1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;773&quot; height=&quot;1058&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;1058&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Model을 사용하기 위해서는 해당 LLM의 공급사과 Key를 발급받아 입력해주면 관련 Model을 사용할 수 있게됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;929&quot; data-origin-height=&quot;1061&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lCPwJ/btsM1hGwP1m/IgLJMlgCD5Nz4UuIjeSSj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lCPwJ/btsM1hGwP1m/IgLJMlgCD5Nz4UuIjeSSj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lCPwJ/btsM1hGwP1m/IgLJMlgCD5Nz4UuIjeSSj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlCPwJ%2FbtsM1hGwP1m%2FIgLJMlgCD5Nz4UuIjeSSj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;929&quot; height=&quot;1061&quot; data-origin-width=&quot;929&quot; data-origin-height=&quot;1061&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 제공되는 평가지표로는 Hallucination, Moderation, AnswerRelevance가 있고, 기호에 따라 직접 프롬프트를 조정하거나 Custom LLM-as-judge를 만들어서 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;784&quot; data-origin-height=&quot;1058&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zCXcD/btsMZ6y0iEo/kkqKuk6ewTDfOyfs28ttjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zCXcD/btsMZ6y0iEo/kkqKuk6ewTDfOyfs28ttjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zCXcD/btsMZ6y0iEo/kkqKuk6ewTDfOyfs28ttjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzCXcD%2FbtsMZ6y0iEo%2FkkqKuk6ewTDfOyfs28ttjK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;784&quot; height=&quot;1058&quot; data-origin-width=&quot;784&quot; data-origin-height=&quot;1058&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 제공해주는 Metric이 의미하는 바는 아래와 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Hallucination&lt;/b&gt;: LLM 출력에 환각 정보 포함 여부 검사&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Moderation&lt;/b&gt;: LLM 출력에 부적절한 내용 포함 여부 검사&lt;/li&gt;
&lt;li&gt;&lt;b&gt;AnswerRelevance&lt;/b&gt;: LLM 출력이 주어진 맥락과 관련성 검사&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 생성된 답변이 context와 관련이 있는지 확인하고 싶어서 &lt;b&gt;AnswerRelevance&lt;/b&gt;를 선택했습니다. 그리고 이제 하단에 input, output, context에 해당하는 값을 mapping을 시켜줘야하는데, 종류가 다양하고 자세한 설명이 없어서 실행 결과값이 계속 0으로 나와서 혼란을 겪었습니다. 여러 시행착오끝에 context는 output.documents를 선택하거나 기본 제공하는 값은 아니지만 input.context라고 입력해도 정상 작동하는 걸 확인했습니다. input은 question이 아닌 query로 하고 output은 answer를 선택하니 정상적으로 작동하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2303&quot; data-origin-height=&quot;462&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qTWdQ/btsM0t8Uh8u/FpGG9fUFCkdiYptc79Ean1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qTWdQ/btsM0t8Uh8u/FpGG9fUFCkdiYptc79Ean1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qTWdQ/btsM0t8Uh8u/FpGG9fUFCkdiYptc79Ean1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqTWdQ%2FbtsM0t8Uh8u%2FFpGG9fUFCkdiYptc79Ean1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2303&quot; height=&quot;462&quot; data-origin-width=&quot;2303&quot; data-origin-height=&quot;462&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평가가 완료되면 &quot;Feedback scores&quot; 섹션에서 점수와 함께 상세한 평가 이유를 확인할 수 있습니다. 이를 통해 LLM 응답의 품질을 객관적으로 분석할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;385&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sLO6x/btsM0q5nPHW/Y27twoWWH27IudswpvWIGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sLO6x/btsM0q5nPHW/Y27twoWWH27IudswpvWIGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sLO6x/btsM0q5nPHW/Y27twoWWH27IudswpvWIGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsLO6x%2FbtsM0q5nPHW%2FY27twoWWH27IudswpvWIGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1906&quot; height=&quot;385&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;385&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Opik의 Online Evaluation 기능을 활용하면 배포된 LLM 애플리케이션의 성능을 실시간으로 모니터링하고 평가할 수 있습니다. 현재는 AnswerRelevance 메트릭만 활용했지만, 향후에는 다양한 메트릭과 커스텀 평가 방식을 적용해볼 예정입니다. 또한 별도의 데이터 세트를 구축하여 더 체계적인 평가도 진행할 계획입니다.&lt;/p&gt;</description>
      <category>LLM Engineering/LLMops</category>
      <category>LLM</category>
      <category>llm 평가</category>
      <category>llm-as-judge</category>
      <category>metric</category>
      <category>opik</category>
      <category>SCORE</category>
      <category>trace</category>
      <category>평가</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/81</guid>
      <comments>https://striver.tistory.com/entry/LLMops-Opik%EC%9C%BC%EB%A1%9C-LLM-%ED%8F%89%EA%B0%80%ED%95%98%EA%B8%B0#entry81comment</comments>
      <pubDate>Fri, 28 Mar 2025 17:47:30 +0900</pubDate>
    </item>
    <item>
      <title>[LLM] context 기반 답변 비교</title>
      <link>https://striver.tistory.com/entry/LLM-context-%EA%B8%B0%EB%B0%98-%EB%8B%B5%EB%B3%80-%EB%B9%84%EA%B5%90</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 재무제표 데이터로 DB를 구축했으니 이 데이터를 이용해 사용자 쿼리에 대해 답변을 생성해 보도록하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. DB 조회해서 데이터 가져오기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;psycopg2&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;connect()을 사용해 디비에 연결된 상황고 query 변수로 사용자가 원하는 해외 기업이 input으로 들어왔다고 가정하고 진행하도록 하겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742966407843&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def get_company_financial_data(conn, ticker):
    &quot;&quot;&quot;특정 기업의 모든 재무 데이터 조회&quot;&quot;&quot;
    cursor = conn.cursor()

    # 기업 정보 확인
    cursor.execute(
        &quot;SELECT ticker, company_name FROM companies WHERE ticker = %s&quot;, (ticker,)
    )
    company = cursor.fetchone()

    if not company:
        return None

    ticker, company_name = company

    # 재무 데이터 조회
    cursor.execute(
        &quot;&quot;&quot;
        SELECT rt.name as report_type, m.name as metric, fd.value
        FROM financial_data fd
        JOIN metrics m ON fd.metric_id = m.id
        JOIN report_types rt ON m.report_type_id = rt.id
        WHERE fd.ticker = %s
        ORDER BY rt.name, m.name
    &quot;&quot;&quot;,
        (ticker,),
    )

    results = cursor.fetchall()

    # 결과 포맷팅
    formatted_data = {&quot;ticker&quot;: ticker, &quot;company_name&quot;: company_name, &quot;financials&quot;: {}}

    for report_type, metric, value in results:
        if report_type not in formatted_data[&quot;financials&quot;]:
            formatted_data[&quot;financials&quot;][report_type] = {}

        formatted_data[&quot;financials&quot;][report_type][metric] = value

    return formatted_data

query=&quot;AAPL&quot;

# 예시: 애플(AAPL) 재무 데이터 조회
financial_data = get_company_financial_data(conn, query)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 코드로 재무데이터를 조회하고 financial_data를 출력해보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742966511395&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{'balance_sheet': {'Cash On Hand': '$53,775',
  'Long Term Debt': '$83,956',
  'Share Holder Equity': '$66,758',
  'Total Assets': '$344,085',
  'Total Current Assets': '$133,240',
  'Total Current Liabilities': '$144,365',
  'Total Liabilities': '$277,327',
  'Total Liabilities And Share Holders Equity': '$344,085'},
 'cash_flow': {'Cash Flow From Financial Activities': '$-39,371',
  'Cash Flow From Investing Activities': '$9,792',
  'Cash Flow From Operating Activities': '$29,935',
  'Net Cash Flow': '$356'},
 'financial_ratios': {'Book Value Per Share': '4.4385',
  'Current Ratio': '0.9229',
  'Debt/Equity Ratio': '1.45',
  'Free Cash Flow Per Share': '1.7817',
  'Gross Margin': '46.8825',
  'Net Profit Margin': '29.2277',
  'Operating Margin': '34.4586',
  'ROA - Return On Assets': '10.5584',
  'ROE - Return On Equity': '54.4204'},
 'income_statement': {'Basic EPS': '$2.41',
  'EBIT': '$42,832',
  'EBITDA': '$45,912',
  'EPS - Earnings Per Share': '$2.40',
  'Gross Profit': '$58,275',
  'Net Income': '$36,330',
  'Operating Income': '$42,832',
  'Pre-Tax Income': '$42,584',
  'Revenue': '$124,300'}}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Json 형태로 데이터들이 잘 조회 된 모습을 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. PromptTemplate으로 prompt 만들기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추후에 chain을 구성하기 위해 prompt를 PromptTemplate()를 사용해서 객체로 구성해주겠습니다. 그전에 먼저 .env 파일에 llm으로 사용할 OPENAI_API_KEY 에 대한 값을 저장해주고 load_dotenv()를 실행해줍니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742966760722&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from dotenv import load_dotenv

load_dotenv()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿼리 조회를 해서 가져온 재무제표 데이터를 활용해 답변을 하는지 확인하기 위해, 재무제표 데이터를 사용했을때와 사용하지 않았을때 2개의 프롬프트로 나눠서 chain을 구성해보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742966976211&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate

llm = ChatOpenAI(model=&quot;gpt-4o-mini&quot;)

template_1 = &quot;너는 해외 금융 투자 전문가야. {ticker} 종목에 대해서 투자 판단을 내릴 수 있도록 보고서 형태로 일목요연하게 정리해줘.&quot;
template_2 = &quot;너는 해외 금융 투자 전문가야. {ticker} 종목에 대해서 {context}를 활용해서 투자 판단을 내릴 수 있도록 보고서 형태로 일목요연하게 정리해줘.&quot;

prompt_1 = PromptTemplate(
    template=template_1,
    input_variables=[&quot;ticker&quot;],
)

prompt_2 = PromptTemplate(
    template=template_2,
    input_variables=[&quot;ticker&quot;, &quot;context&quot;],
)

chain_1 = prompt_1 | llm
chain_2 = prompt_2 | llm&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;chain을 invoke()로 실행할때 프롬프트에에 지정한 input_variables의 값을 딕셔너리{ } 형태로 전달해주어야합니다. 그리고 content key 값을 출력하면 응답을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742967048812&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;chain_1.invoke({&quot;ticker&quot;: financial_data[&quot;ticker&quot;]}).content&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 확인해보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;##&amp;nbsp;AAPL&amp;nbsp;(Apple&amp;nbsp;Inc.)&amp;nbsp;투자&amp;nbsp;보고서&lt;br /&gt;&lt;br /&gt;###&amp;nbsp;1.&amp;nbsp;개요&lt;br /&gt;-&amp;nbsp;**회사명**:&amp;nbsp;Apple&amp;nbsp;Inc.&lt;br /&gt;-&amp;nbsp;**티커**:&amp;nbsp;AAPL&lt;br /&gt;-&amp;nbsp;**산업&amp;nbsp;분야**:&amp;nbsp;소비자&amp;nbsp;전자제품&amp;nbsp;및&amp;nbsp;기술&lt;br /&gt;-&amp;nbsp;**상장시장**:&amp;nbsp;나스닥&lt;br /&gt;&lt;br /&gt;###&amp;nbsp;2.&amp;nbsp;최근&amp;nbsp;성과&lt;br /&gt;-&amp;nbsp;**주가동향**:&amp;nbsp;최근&amp;nbsp;1년간&amp;nbsp;AAPL&amp;nbsp;주가는&amp;nbsp;약&amp;nbsp;X%&amp;nbsp;상승했습니다.&amp;nbsp;(X%는&amp;nbsp;실제&amp;nbsp;데이터를&amp;nbsp;기반으로&amp;nbsp;수정&amp;nbsp;필요)&lt;br /&gt;-&amp;nbsp;**시가총액**:&amp;nbsp;약&amp;nbsp;$X&amp;nbsp;trillion&amp;nbsp;(마찬가지로&amp;nbsp;데이터&amp;nbsp;보강&amp;nbsp;필요)&lt;br /&gt;-&amp;nbsp;**P/E&amp;nbsp;비율**:&amp;nbsp;AAPL의&amp;nbsp;현재&amp;nbsp;주가수익비율(P/E)은&amp;nbsp;X로,&amp;nbsp;이는&amp;nbsp;같은&amp;nbsp;업종&amp;nbsp;내&amp;nbsp;평균인&amp;nbsp;Y와&amp;nbsp;비교할&amp;nbsp;때&amp;nbsp;(고평가/저평가)되며,&amp;nbsp;성장&amp;nbsp;잠재력을&amp;nbsp;반영합니다.&lt;br /&gt;&lt;br /&gt;###&amp;nbsp;3.&amp;nbsp;재무&amp;nbsp;현황&lt;br /&gt;-&amp;nbsp;**매출&amp;nbsp;성장률**:&amp;nbsp;최근&amp;nbsp;분기&amp;nbsp;매출은&amp;nbsp;$X&amp;nbsp;billion으로,&amp;nbsp;전년&amp;nbsp;대비&amp;nbsp;Y%&amp;nbsp;증가하였습니다.&lt;br /&gt;-&amp;nbsp;**순이익**:&amp;nbsp;순이익은&amp;nbsp;$X&amp;nbsp;billion이며,&amp;nbsp;이는&amp;nbsp;증가&amp;nbsp;추세를&amp;nbsp;보이고&amp;nbsp;있습니다.&lt;br /&gt;-&amp;nbsp;**부채&amp;nbsp;비율**:&amp;nbsp;총&amp;nbsp;부채는&amp;nbsp;X%로&amp;nbsp;상대적으로&amp;nbsp;안전한&amp;nbsp;수준입니다.&lt;br /&gt;-&amp;nbsp;**현금&amp;nbsp;흐름**:&amp;nbsp;AAPL은&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;기여하고&amp;nbsp;있습니다.&lt;br /&gt;&lt;br /&gt;###&amp;nbsp;4.&amp;nbsp;시장&amp;nbsp;동향&lt;br /&gt;-&amp;nbsp;**경쟁사&amp;nbsp;분석**:&amp;nbsp;주요&amp;nbsp;경쟁사로는&amp;nbsp;삼성전자,&amp;nbsp;구글,&amp;nbsp;마이크로소프트&amp;nbsp;등이&amp;nbsp;있으며,&amp;nbsp;이들과의&amp;nbsp;비교에서&amp;nbsp;AAPL의&amp;nbsp;시장&amp;nbsp;점유율이&amp;nbsp;X%로&amp;nbsp;나타났습니다.&lt;br /&gt;-&amp;nbsp;**소비자&amp;nbsp;트렌드**:&amp;nbsp;소비자&amp;nbsp;기술에&amp;nbsp;대한&amp;nbsp;수요&amp;nbsp;증가와&amp;nbsp;맞물려&amp;nbsp;AAPL의&amp;nbsp;아이폰,&amp;nbsp;아이패드&amp;nbsp;및&amp;nbsp;기타&amp;nbsp;제품의&amp;nbsp;판매가&amp;nbsp;호조를&amp;nbsp;보이고&amp;nbsp;있습니다.&lt;br /&gt;&lt;br /&gt;###&amp;nbsp;5.&amp;nbsp;위험&amp;nbsp;요인&lt;br /&gt;-&amp;nbsp;**경쟁&amp;nbsp;심화**:&amp;nbsp;기술&amp;nbsp;발전과&amp;nbsp;시장의&amp;nbsp;변화로&amp;nbsp;인해&amp;nbsp;경쟁이&amp;nbsp;심화되고&amp;nbsp;있습니다.&lt;br /&gt;-&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;줄&amp;nbsp;수&amp;nbsp;있습니다.&lt;br /&gt;-&amp;nbsp;**공급망&amp;nbsp;문제**:&amp;nbsp;코로나19&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;있습니다.&lt;br /&gt;&lt;br /&gt;###&amp;nbsp;6.&amp;nbsp;결론&amp;nbsp;및&amp;nbsp;투자&amp;nbsp;판단&lt;br /&gt;AAPL은&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;경직된&amp;nbsp;시장과&amp;nbsp;경쟁&amp;nbsp;심화,&amp;nbsp;거시경제적&amp;nbsp;요인들에&amp;nbsp;유의해야&amp;nbsp;합니다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;**투자&amp;nbsp;판단**:&amp;nbsp;&lt;br /&gt;-&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;기대되는&amp;nbsp;경우.&lt;br /&gt;-&amp;nbsp;**유지**:&amp;nbsp;주가가&amp;nbsp;상당히&amp;nbsp;상승하여&amp;nbsp;현재&amp;nbsp;경쟁&amp;nbsp;우위가&amp;nbsp;약해질&amp;nbsp;경우&amp;nbsp;보유를&amp;nbsp;유지.&lt;br /&gt;-&amp;nbsp;**매도**:&amp;nbsp;시장&amp;nbsp;불확실성&amp;nbsp;증가&amp;nbsp;시&amp;nbsp;판별이&amp;nbsp;필요한&amp;nbsp;매도&amp;nbsp;시점.&lt;br /&gt;&lt;br /&gt;###&amp;nbsp;추가&amp;nbsp;권장&amp;nbsp;사항&lt;br /&gt;-&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;있는&amp;nbsp;데이터에&amp;nbsp;기반한&amp;nbsp;평가를&amp;nbsp;지속적으로&amp;nbsp;업데이트하는&amp;nbsp;것이&amp;nbsp;중요합니다.&lt;br /&gt;&lt;br /&gt;---&amp;nbsp;&lt;br /&gt;&lt;br /&gt;(상기&amp;nbsp;자료의&amp;nbsp;숫자는&amp;nbsp;실제&amp;nbsp;시장&amp;nbsp;데이터에&amp;nbsp;따라&amp;nbsp;업데이트&amp;nbsp;필요)&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보고서 형식으로 깔끔하게 정리한것 같지만, 내용을 살펴보면 구체적인 데이터도 없고 투자판단도 확실하게 내리지 않는 모습을 볼 수 있습니다. 이번엔 재무제표 데이터를 활용한 LLM의 답변을 살펴보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742967199988&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;chain_2.invoke(
    {&quot;ticker&quot;: financial_data[&quot;ticker&quot;], &quot;context&quot;: financial_data[&quot;financials&quot;]}
).content&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;똑같이 딕셔너리 형태로 input_variables의 값을 채워주고 결과를 확인해보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;Apple&amp;nbsp;Inc.&amp;nbsp;(AAPL)&amp;nbsp;투자&amp;nbsp;분석&amp;nbsp;보고서&lt;br /&gt;&lt;br /&gt;##&amp;nbsp;1.&amp;nbsp;회사&amp;nbsp;개요&lt;br /&gt;Apple&amp;nbsp;Inc.&amp;nbsp;(AAPL)은&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;서비스&amp;nbsp;분야에서&amp;nbsp;폭넓은&amp;nbsp;제품과&amp;nbsp;서비스를&amp;nbsp;제공합니다.&amp;nbsp;본&amp;nbsp;보고서는&amp;nbsp;AAPL의&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;위해&amp;nbsp;작성되었습니다.&lt;br /&gt;&lt;br /&gt;##&amp;nbsp;2.&amp;nbsp;재무제표&amp;nbsp;요약&lt;br /&gt;&lt;br /&gt;###&amp;nbsp;2.1.&amp;nbsp;대차대조표&amp;nbsp;(Balance&amp;nbsp;Sheet)&lt;br /&gt;-&amp;nbsp;**현금&amp;nbsp;및&amp;nbsp;현금성&amp;nbsp;자산&amp;nbsp;(Cash&amp;nbsp;On&amp;nbsp;Hand)**:&amp;nbsp;$53,775M&lt;br /&gt;-&amp;nbsp;**장기&amp;nbsp;부채&amp;nbsp;(Long&amp;nbsp;Term&amp;nbsp;Debt)**:&amp;nbsp;$83,956M&lt;br /&gt;-&amp;nbsp;**주주&amp;nbsp;자본&amp;nbsp;(Share&amp;nbsp;Holder&amp;nbsp;Equity)**:&amp;nbsp;$66,758M&lt;br /&gt;-&amp;nbsp;**총&amp;nbsp;자산&amp;nbsp;(Total&amp;nbsp;Assets)**:&amp;nbsp;$344,085M&lt;br /&gt;-&amp;nbsp;**총&amp;nbsp;현재&amp;nbsp;자산&amp;nbsp;(Total&amp;nbsp;Current&amp;nbsp;Assets)**:&amp;nbsp;$133,240M&lt;br /&gt;-&amp;nbsp;**총&amp;nbsp;현재&amp;nbsp;부채&amp;nbsp;(Total&amp;nbsp;Current&amp;nbsp;Liabilities)**:&amp;nbsp;$144,365M&lt;br /&gt;-&amp;nbsp;**총&amp;nbsp;부채&amp;nbsp;(Total&amp;nbsp;Liabilities)**:&amp;nbsp;$277,327M&lt;br /&gt;-&amp;nbsp;**부채&amp;nbsp;및&amp;nbsp;주주&amp;nbsp;자본&amp;nbsp;합계&amp;nbsp;(Total&amp;nbsp;Liabilities&amp;nbsp;And&amp;nbsp;Share&amp;nbsp;Holder&amp;nbsp;Equity)**:&amp;nbsp;$344,085M&lt;br /&gt;&lt;br /&gt;###&amp;nbsp;2.2.&amp;nbsp;현금&amp;nbsp;흐름&amp;nbsp;(Cash&amp;nbsp;Flow)&lt;br /&gt;-&amp;nbsp;**재무&amp;nbsp;활동으로&amp;nbsp;인한&amp;nbsp;현금&amp;nbsp;흐름&amp;nbsp;(Cash&amp;nbsp;Flow&amp;nbsp;From&amp;nbsp;Financial&amp;nbsp;Activities)**:&amp;nbsp;$-39,371M&lt;br /&gt;-&amp;nbsp;**투자&amp;nbsp;활동으로&amp;nbsp;인한&amp;nbsp;현금&amp;nbsp;흐름&amp;nbsp;(Cash&amp;nbsp;Flow&amp;nbsp;From&amp;nbsp;Investing&amp;nbsp;Activities)**:&amp;nbsp;$9,792M&lt;br /&gt;-&amp;nbsp;**운영&amp;nbsp;활동으로&amp;nbsp;인한&amp;nbsp;현금&amp;nbsp;흐름&amp;nbsp;(Cash&amp;nbsp;Flow&amp;nbsp;From&amp;nbsp;Operating&amp;nbsp;Activities)**:&amp;nbsp;$29,935M&lt;br /&gt;-&amp;nbsp;**순&amp;nbsp;현금&amp;nbsp;흐름&amp;nbsp;(Net&amp;nbsp;Cash&amp;nbsp;Flow)**:&amp;nbsp;$356M&lt;br /&gt;&lt;br /&gt;###&amp;nbsp;2.3.&amp;nbsp;손익계산서&amp;nbsp;(Income&amp;nbsp;Statement)&lt;br /&gt;-&amp;nbsp;**기본&amp;nbsp;주당순이익&amp;nbsp;(Basic&amp;nbsp;EPS)**:&amp;nbsp;$2.41&lt;br /&gt;-&amp;nbsp;**EBIT&amp;nbsp;(세전&amp;nbsp;영업&amp;nbsp;이익)**:&amp;nbsp;$42,832M&lt;br /&gt;-&amp;nbsp;**EBITDA&amp;nbsp;(상각&amp;nbsp;전&amp;nbsp;영업&amp;nbsp;이익)**:&amp;nbsp;$45,912M&lt;br /&gt;-&amp;nbsp;**주당&amp;nbsp;순이익&amp;nbsp;(EPS&amp;nbsp;-&amp;nbsp;Earnings&amp;nbsp;Per&amp;nbsp;Share)**:&amp;nbsp;$2.40&lt;br /&gt;-&amp;nbsp;**총&amp;nbsp;수익&amp;nbsp;(Revenue)**:&amp;nbsp;$124,300M&lt;br /&gt;-&amp;nbsp;**순이익&amp;nbsp;(Net&amp;nbsp;Income)**:&amp;nbsp;$36,330M&lt;br /&gt;&lt;br /&gt;##&amp;nbsp;3.&amp;nbsp;재무&amp;nbsp;비율&amp;nbsp;분석&lt;br /&gt;-&amp;nbsp;**주당&amp;nbsp;장부가치&amp;nbsp;(Book&amp;nbsp;Value&amp;nbsp;Per&amp;nbsp;Share)**:&amp;nbsp;$4.44&lt;br /&gt;-&amp;nbsp;**유동&amp;nbsp;비율&amp;nbsp;(Current&amp;nbsp;Ratio)**:&amp;nbsp;0.92&lt;br /&gt;-&amp;nbsp;**부채/자본&amp;nbsp;비율&amp;nbsp;(Debt/Equity&amp;nbsp;Ratio)**:&amp;nbsp;1.45&lt;br /&gt;-&amp;nbsp;**자유&amp;nbsp;현금&amp;nbsp;흐름&amp;nbsp;주당&amp;nbsp;(Free&amp;nbsp;Cash&amp;nbsp;Flow&amp;nbsp;Per&amp;nbsp;Share)**:&amp;nbsp;$1.78&lt;br /&gt;-&amp;nbsp;**총&amp;nbsp;이익률&amp;nbsp;(Gross&amp;nbsp;Margin)**:&amp;nbsp;46.88%&lt;br /&gt;-&amp;nbsp;**순&amp;nbsp;이익률&amp;nbsp;(Net&amp;nbsp;Profit&amp;nbsp;Margin)**:&amp;nbsp;29.23%&lt;br /&gt;-&amp;nbsp;**영업&amp;nbsp;이익률&amp;nbsp;(Operating&amp;nbsp;Margin)**:&amp;nbsp;34.46%&lt;br /&gt;-&amp;nbsp;**자산&amp;nbsp;수익률&amp;nbsp;(ROA)**:&amp;nbsp;10.56%&lt;br /&gt;-&amp;nbsp;**자기&amp;nbsp;자본&amp;nbsp;수익률&amp;nbsp;(ROE)**:&amp;nbsp;54.42%&lt;br /&gt;&lt;br /&gt;##&amp;nbsp;4.&amp;nbsp;투자&amp;nbsp;판단&lt;br /&gt;###&amp;nbsp;4.1.&amp;nbsp;강점&lt;br /&gt;-&amp;nbsp;**높은&amp;nbsp;수익성**:&amp;nbsp;AAPL의&amp;nbsp;순&amp;nbsp;이익률(29.23%)과&amp;nbsp;영업&amp;nbsp;이익률(34.46%)은&amp;nbsp;업계&amp;nbsp;평균보다&amp;nbsp;높아&amp;nbsp;안정적인&amp;nbsp;수익&amp;nbsp;구조를&amp;nbsp;나타냅니다.&lt;br /&gt;-&amp;nbsp;**강력한&amp;nbsp;재무&amp;nbsp;구조**:&amp;nbsp;높은&amp;nbsp;자산&amp;nbsp;총액($344,085M)과&amp;nbsp;대비해&amp;nbsp;상대적으로&amp;nbsp;적은&amp;nbsp;부채(총&amp;nbsp;부채&amp;nbsp;$277,327M)는&amp;nbsp;재무적&amp;nbsp;안정성을&amp;nbsp;보여줍니다.&amp;nbsp;&lt;br /&gt;-&amp;nbsp;**높은&amp;nbsp;ROE**:&amp;nbsp;54.42%의&amp;nbsp;자기&amp;nbsp;자본&amp;nbsp;수익률은&amp;nbsp;주주들에게&amp;nbsp;높은&amp;nbsp;이익을&amp;nbsp;가져다줄&amp;nbsp;잠재력을&amp;nbsp;보여줍니다.&lt;br /&gt;&lt;br /&gt;###&amp;nbsp;4.2.&amp;nbsp;우려&amp;nbsp;사항&lt;br /&gt;-&amp;nbsp;**부채&amp;nbsp;비율**:&amp;nbsp;부채/자본&amp;nbsp;비율이&amp;nbsp;1.45로&amp;nbsp;상대적으로&amp;nbsp;높아,&amp;nbsp;향후&amp;nbsp;이자&amp;nbsp;지급&amp;nbsp;능력에&amp;nbsp;대한&amp;nbsp;우려를&amp;nbsp;초래할&amp;nbsp;수&amp;nbsp;있습니다.&lt;br /&gt;-&amp;nbsp;**유동성&amp;nbsp;문제**:&amp;nbsp;유동&amp;nbsp;비율(0.92)이&amp;nbsp;1&amp;nbsp;이하로,&amp;nbsp;단기&amp;nbsp;채무&amp;nbsp;상환&amp;nbsp;능력에&amp;nbsp;일부&amp;nbsp;부담이&amp;nbsp;있을&amp;nbsp;수&amp;nbsp;있습니다.&lt;br /&gt;&lt;br /&gt;###&amp;nbsp;4.3.&amp;nbsp;결론&lt;br /&gt;Apple&amp;nbsp;Inc.&amp;nbsp;(AAPL)은&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;부채&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;볼&amp;nbsp;때,&amp;nbsp;AAPL은&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;동향과&amp;nbsp;경쟁&amp;nbsp;환경&amp;nbsp;분석이&amp;nbsp;필요합니다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;##&amp;nbsp;5.&amp;nbsp;추천&lt;br /&gt;AAPL&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;리스크&amp;nbsp;관리를&amp;nbsp;권장합니다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 재무제표 데이터를 바탕으로 더 확실하게 수치화 된 보고서가 생성됐고, 데이터를 분석해서 기존 chain_1 대비 투자에 대한 조언과 적당한 판단도 내린 모습을 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. Prompt 템플릿 관리하기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;꼭 필요한 과정은 아니지만, 우리가 추후에 프롬프트를 계속 수정해간다거나 여러 종류의 프롬프트가 생겼을때 이를 LangChain Hub를 활용해서 쉽게 관리하는 방법을 간단하게 살펴보겠습니다. LangChain Hub를 사용하기 위해서는 LangSmith에서 API key를 꼭 발급받아줘야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2532&quot; data-origin-height=&quot;1128&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dnT33u/btsMVWqyhaC/AuP1MEdRMvRszCkDSqxFbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dnT33u/btsMVWqyhaC/AuP1MEdRMvRszCkDSqxFbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dnT33u/btsMVWqyhaC/AuP1MEdRMvRszCkDSqxFbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdnT33u%2FbtsMVWqyhaC%2FAuP1MEdRMvRszCkDSqxFbk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2532&quot; height=&quot;1128&quot; data-origin-width=&quot;2532&quot; data-origin-height=&quot;1128&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangChain Hub에는 내가 작성한 프롬프트를 자유롭게 업로드할 수 있고, 다른 사람이 작성하고 공유한 프롬프트도 손쉽게 사용할 수 있습니다. 우측에 보면 목적별 프롬프트가 다양하게 존재하는 모습을 볼 수 있습니다. 프롬프트를 가져오는 코드는 langchain을 활용해 아주&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 작성할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742968918512&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langchain import hub

# 가장 최신 버전의 프롬프트를 가져옵니다.
prompt = hub.pull(&quot;rlm/rag-prompt&quot;)

# 특정 버전의 프롬프트를 가져오려면 버전 해시를 지정하세요
prompt = hub.pull(&quot;rlm/rag-prompt:50442af1&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 제가 작성한 프롬프트를 Langchain Hub에 업로드해서 확인해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742970262195&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langchain import hub

# 프롬프트를 허브에 업로드합니다.
hub.push(&quot;{repo_full_name}&quot;, prompt_2)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드는 굉장히 간단한데 문제가 조금 있었습니다. repo_full_name에 자신의 langsmith Id를 입력해야하는데 이 부분이 조금 어려웠습니다. 초기에는 Langsmith에 ID가 만들어지지않는게 default이기때문에 ID를 새로 만들어줘야합니다. 우선 공유되어있는 프롬프트중 하나를 아무거나 선택하고 우측상단에 Fork를 눌러줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1124&quot; data-origin-height=&quot;1103&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XLImD/btsMVTUZE8L/TuJb9xCxKm4IpJoSCcgkiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XLImD/btsMVTUZE8L/TuJb9xCxKm4IpJoSCcgkiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XLImD/btsMVTUZE8L/TuJb9xCxKm4IpJoSCcgkiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXLImD%2FbtsMVTUZE8L%2FTuJb9xCxKm4IpJoSCcgkiK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1124&quot; height=&quot;1103&quot; data-origin-width=&quot;1124&quot; data-origin-height=&quot;1103&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;prmpt name을 자유롭게 입력해주고 Fork를 누릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;289&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctrHWT/btsMWGU6I5V/8K1EhiCIYMy3XLO3IV15Pk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctrHWT/btsMWGU6I5V/8K1EhiCIYMy3XLO3IV15Pk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctrHWT/btsMWGU6I5V/8K1EhiCIYMy3XLO3IV15Pk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctrHWT%2FbtsMWGU6I5V%2F8K1EhiCIYMy3XLO3IV15Pk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;506&quot; height=&quot;289&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;289&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 여기서 Langsmith id를 입력하면 됩니다. 한번 설정하면 바꿀 수 없으니 신중하게 입력하면 되겠습니다. 그리고나서 다시 코드로 돌아가서 hub.push를 진행해보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1833&quot; data-origin-height=&quot;519&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wnc4T/btsMVTUZ29F/FoufrwC3XHfDMBgKHeLvAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wnc4T/btsMVTUZ29F/FoufrwC3XHfDMBgKHeLvAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wnc4T/btsMVTUZ29F/FoufrwC3XHfDMBgKHeLvAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwnc4T%2FbtsMVTUZ29F%2FFoufrwC3XHfDMBgKHeLvAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1833&quot; height=&quot;519&quot; data-origin-width=&quot;1833&quot; data-origin-height=&quot;519&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공적으로 제 Langchain Hub 레파지토리에 프롬프트가 생성된 모습입니다. 혹시 몰라 hub.pull을 사용해 프롬프트가 잘 가져와지는지까지 테스트해보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1562&quot; data-origin-height=&quot;245&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pfKf4/btsMWUZVUPy/43Um7tFs3ezL78VWK3aPnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pfKf4/btsMWUZVUPy/43Um7tFs3ezL78VWK3aPnK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pfKf4/btsMWUZVUPy/43Um7tFs3ezL78VWK3aPnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpfKf4%2FbtsMWUZVUPy%2F43Um7tFs3ezL78VWK3aPnK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1562&quot; height=&quot;245&quot; data-origin-width=&quot;1562&quot; data-origin-height=&quot;245&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프롬프트가 잘 가져와지는 모습을 볼 수 있었습니다. 이제 앞으로 Langchain Hub를 사용해 목적에 맞는 프롬프트를 손쉽게 가져다 사용할 수 있고, 내가 사용하는 프롬프트들도 버전별로 업로드하면서 편리하게 유지보수할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/teddylee777/langchain-kr/blob/main/02-Prompt/01-PromptTemplate.ipynb&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/teddylee777/langchain-kr/blob/main/02-Prompt/01-PromptTemplate.ipynb&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Activities/FinAgent Lab</category>
      <category>chain</category>
      <category>langchain hub</category>
      <category>LangSmith</category>
      <category>LLM</category>
      <category>prompt template</category>
      <category>query</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/80</guid>
      <comments>https://striver.tistory.com/entry/LLM-context-%EA%B8%B0%EB%B0%98-%EB%8B%B5%EB%B3%80-%EB%B9%84%EA%B5%90#entry80comment</comments>
      <pubDate>Thu, 27 Mar 2025 15:35:48 +0900</pubDate>
    </item>
    <item>
      <title>[DB] Postgresql DB 구축하기</title>
      <link>https://striver.tistory.com/entry/DB-Postgresql-DB-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;일전에 크롤링을 통해 얻었던 재무제표 데이터를 활용해 로컬에서 postgresql DB를 구축해 자연어 쿼리에서 엔티티를 추출했다는 가정하에, 특정 기업에 대한 데이터를 디비에서 조회해서 LLM에게 context로 제공해 답변을 생성해보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. Postgresql 설치&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 컨테이너를 사용하지않고 가장 기본적인 로컬 환경에서 실행을 할 것이기 때문에 아래 명령어로 먼저 Postgresql 서버가 실행중인지 확인해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742905814932&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;brew services list&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기엔 당연히 postgresql이 안나올것이고 실행을 위해서는 &quot;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;brew services start postgresql&lt;/b&gt;&lt;/span&gt;&quot; 명령어를 입력해줘야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;366&quot; data-origin-height=&quot;31&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Yaqce/btsMVdTgzmA/2xkK0uYkw64l4ZZQk1S1NK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Yaqce/btsMVdTgzmA/2xkK0uYkw64l4ZZQk1S1NK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Yaqce/btsMVdTgzmA/2xkK0uYkw64l4ZZQk1S1NK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYaqce%2FbtsMVdTgzmA%2F2xkK0uYkw64l4ZZQk1S1NK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;366&quot; height=&quot;31&quot; data-origin-width=&quot;366&quot; data-origin-height=&quot;31&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행후 사진과같은 에러가 발생했다면, postgresql이 설치되어있지 않아서 생긴 에러이기 때문에 하단 명령어로 설치해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742905910505&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;brew install postgresql&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 다시 &quot;brew services start postgresql&quot; 명령어를 입력하고 &quot;brew services list&quot;로 확인해보면 로컬에서 postgresql이 정상적으로 실행되고 있는 모습을 볼 수 있습니다. 나중에는 이렇게 로컬에 다운로드 받아서 실행시키는게 아니라 도커 컨테이너 환경에서 다운로드받아서 실행하는 것도 일반적으로 사용되는 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. server &amp;amp; DB create&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;postgresql을 설치하고 실행했으니 이제 내부에 디비를 만들고 테이블을 만들도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;847&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cncNpw/btsMWCRXXHG/ecSbnvhC7eKVQXeIO5xYt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cncNpw/btsMWCRXXHG/ecSbnvhC7eKVQXeIO5xYt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cncNpw/btsMWCRXXHG/ecSbnvhC7eKVQXeIO5xYt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcncNpw%2FbtsMWCRXXHG%2FecSbnvhC7eKVQXeIO5xYt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1416&quot; height=&quot;847&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;847&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 더 시각적으로 편하게 작업하기 위해 GUI 도구인 pgAdmin을 설치해고 실행해주었습니다. 가장 처음 실행 시켰을때 화면인데, 여기서 로컬에서 실행한 postgresql 서버를 연결해줍니다. Servers -&amp;gt; Register -&amp;gt; server... 를 클릭해줍니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;212&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/C8uth/btsMXaN8j06/gYgNOc2752wYOqVfk7b1Q1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/C8uth/btsMXaN8j06/gYgNOc2752wYOqVfk7b1Q1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/C8uth/btsMXaN8j06/gYgNOc2752wYOqVfk7b1Q1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FC8uth%2FbtsMXaN8j06%2FgYgNOc2752wYOqVfk7b1Q1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;451&quot; height=&quot;212&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;212&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버의 이름은 자신이 하고싶은 이름을 지정해주면됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;697&quot; data-origin-height=&quot;544&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZL6pg/btsMWsPx9yi/5EvVcuLHfIrkqpQ3C1weU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZL6pg/btsMWsPx9yi/5EvVcuLHfIrkqpQ3C1weU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZL6pg/btsMWsPx9yi/5EvVcuLHfIrkqpQ3C1weU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZL6pg%2FbtsMWsPx9yi%2F5EvVcuLHfIrkqpQ3C1weU0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;697&quot; height=&quot;544&quot; data-origin-width=&quot;697&quot; data-origin-height=&quot;544&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬에서 실행시킨 postgresql 서버에 연결할 것이기 때문에 Host name은 localhost로 하고 별도의 설정을 해주지 않았기 때문에 Username은 기본적으로 현재 macOS 사용자이름이 기본값이고 Password는 없기때문에 나중에 값을 입력해주면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;697&quot; data-origin-height=&quot;542&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dzMEdG/btsMVwLQgCQ/B2klhCnC3eFvxwRihx4E3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dzMEdG/btsMVwLQgCQ/B2klhCnC3eFvxwRihx4E3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dzMEdG/btsMVwLQgCQ/B2klhCnC3eFvxwRihx4E3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdzMEdG%2FbtsMVwLQgCQ%2FB2klhCnC3eFvxwRihx4E3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;697&quot; height=&quot;542&quot; data-origin-width=&quot;697&quot; data-origin-height=&quot;542&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 이제 &quot;financial_data&quot;라고 하는 새로운 server가 생긴 모습을 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1419&quot; data-origin-height=&quot;549&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxYMmq/btsMVS14Yd9/3NdqucSWv5XQ2SdnmZAAr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxYMmq/btsMVS14Yd9/3NdqucSWv5XQ2SdnmZAAr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxYMmq/btsMVS14Yd9/3NdqucSWv5XQ2SdnmZAAr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxYMmq%2FbtsMVS14Yd9%2F3NdqucSWv5XQ2SdnmZAAr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1419&quot; height=&quot;549&quot; data-origin-width=&quot;1419&quot; data-origin-height=&quot;549&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 새 서버에 DB와 Table을 생성해주도록 하겠습니다. 기본적으로 Postgresql은 기본 제공해주는 postgres DB에 먼저 연결한 후, 사용자가 원하는 새로운 DB를 생성한 다음, 해당 데이터베이스에 다시 연결하는 구조입니다. 따라서 먼저 postgres DB에 연결하는 코드를 작성해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742911204733&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import psycopg2
from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT

# 데이터베이스 연결 및 생성
conn = psycopg2.connect(
    database=&quot;postgres&quot;,
    user=&quot;dongseok&quot;,
    password=&quot;qwe123&quot;,  # 실제 비밀번호로 변경
    host=&quot;localhost&quot;,
    port=&quot;5432&quot;
)
conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
cursor = conn.cursor()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CREATE DATABASE 명령은 트랜잭션 안에서 실행할 수 없어서 &quot;conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)&quot; 코드로 트랜잭션 자동 커밋 설정을 추가해줘야합니다. 그리고 이제 내가 원하는 DB를 만들어줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742911350042&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 데이터베이스 생성
db_name = &quot;financial_data&quot;
cursor.execute(f&quot;CREATE DATABASE {db_name}&quot;)
cursor.close()
conn.close()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PostgreSQL에서는 특정 데이터베이스 내부에서 새로운 데이터베이스를 생성하는 것이 아니라, PostgreSQL 서버 자체에서 관리하는 데이터베이스 리스트에 새로운 데이터베이스를 추가하는 방식입니다. 따라서 &quot;CREATE DATABASE financial_data&quot; 명령은 postgres 데이터베이스에 연결된 상태에서 실행하지만, 새로운 financial_data 데이터베이스는 PostgreSQL 서버의 데이터베이스 리스트에 등록됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1417&quot; data-origin-height=&quot;514&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Us8oN/btsMXmOoBlG/cR4crNKqfl8JOPkxOmbV6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Us8oN/btsMXmOoBlG/cR4crNKqfl8JOPkxOmbV6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Us8oN/btsMXmOoBlG/cR4crNKqfl8JOPkxOmbV6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUs8oN%2FbtsMXmOoBlG%2FcR4crNKqfl8JOPkxOmbV6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1417&quot; height=&quot;514&quot; data-origin-width=&quot;1417&quot; data-origin-height=&quot;514&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 새로 만든 financial_data db에 연결하고 테이블을 만들어보도록 하겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742911660188&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 새 데이터베이스에 연결
conn = psycopg2.connect(
    database=db_name,
    user=&quot;dongseok&quot;,
    password=&quot;qwe123&quot;,  # 실제 비밀번호로 변경
    host=&quot;localhost&quot;,
    port=&quot;5432&quot;,
)
cursor = conn.cursor()

# 테이블 생성
cursor.execute(
    &quot;&quot;&quot;
CREATE TABLE companies (
    ticker VARCHAR(20) PRIMARY KEY,
    company_name VARCHAR(200) NOT NULL
);
&quot;&quot;&quot;
)

cursor.execute(
    &quot;&quot;&quot;
CREATE TABLE report_types (
    id SERIAL PRIMARY KEY,
    name VARCHAR(50) UNIQUE NOT NULL
);
&quot;&quot;&quot;
)

cursor.execute(
    &quot;&quot;&quot;
CREATE TABLE metrics (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    report_type_id INTEGER REFERENCES report_types(id),
    UNIQUE(name, report_type_id)
);
&quot;&quot;&quot;
)

cursor.execute(
    &quot;&quot;&quot;
CREATE TABLE financial_data (
    id SERIAL PRIMARY KEY,
    ticker VARCHAR(20) REFERENCES companies(ticker),
    metric_id INTEGER REFERENCES metrics(id),
    value TEXT,
    report_date DATE,
    UNIQUE(ticker, metric_id, report_date)
);
&quot;&quot;&quot;
)

# 보고서 유형 초기 데이터 삽입
report_types = [&quot;income_statement&quot;, &quot;balance_sheet&quot;, &quot;cash_flow&quot;, &quot;financial_ratios&quot;]

for rt in report_types:
    cursor.execute(&quot;INSERT INTO report_types (name) VALUES (%s)&quot;, (rt,))

conn.commit()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 테이블들이 잘 생성된 모습을 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1402&quot; data-origin-height=&quot;746&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/neR9p/btsMXzGT7Tg/eRprft3MilJpZuN2aLCXfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/neR9p/btsMXzGT7Tg/eRprft3MilJpZuN2aLCXfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/neR9p/btsMXzGT7Tg/eRprft3MilJpZuN2aLCXfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FneR9p%2FbtsMXzGT7Tg%2FeRprft3MilJpZuN2aLCXfk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1402&quot; height=&quot;746&quot; data-origin-width=&quot;1402&quot; data-origin-height=&quot;746&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이전에 크롤링했던 재무제표 파일에서 데이터를 파싱해서 테이블에 인덱싱해주면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;818&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJsUNp/btsMWTy9AH8/1f66L8215MlHt5sd4XkYI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJsUNp/btsMWTy9AH8/1f66L8215MlHt5sd4XkYI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJsUNp/btsMWTy9AH8/1f66L8215MlHt5sd4XkYI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJsUNp%2FbtsMWTy9AH8%2F1f66L8215MlHt5sd4XkYI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1408&quot; height=&quot;818&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;818&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿼리에 따른 테이블 조회가 잘 되는 모습을 볼 수 있습니다. 다음에는 이러한 RDBMS에 저장한 값을 LLM에게 context로 줘서 사용자 쿼리에 대해 답변을 생성해보고 더 나아가 pgvector를 활용해 시맨틱 서치도 진행해보도록 하겠습니다.&lt;/p&gt;</description>
      <category>Activities/FinAgent Lab</category>
      <category>PostgreSQL</category>
      <category>postgresqldb</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/79</guid>
      <comments>https://striver.tistory.com/entry/DB-Postgresql-DB-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0#entry79comment</comments>
      <pubDate>Wed, 26 Mar 2025 22:35:13 +0900</pubDate>
    </item>
    <item>
      <title>[crawling] 해외 기업 재무제표 crawling 해오기</title>
      <link>https://striver.tistory.com/entry/FinAgent-Lab-%EC%9E%AC%EB%AC%B4%EC%A0%9C%ED%91%9C-crawling</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;국내 주식을 대상으로 Agent를 개발하던 중 대상이 해외 종목으로 바뀌면서, 기존에 &quot;한경컨센서스&quot;에서 가져오던 재무제표 데이터를 새로운 출처에서 확보해야 했습니다. 이 과정에서 발견한 유용한 해외 종목 데이터 사이트들과 크롤링 과정을 소개합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 해외 기업 데이터 사이트 소개&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) Macrotrends&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Macrotrends는 미국 기업들의 재무정보를 무료로 제공하는 사이트입니다. 중앙 검색창을 통해 원하는 기업을 찾으면 해당 기업의 재무 데이터를 종합적으로 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1873&quot; data-origin-height=&quot;959&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A134n/btsMZHrDuPz/mymPgILobsLSGc1kSKhiZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A134n/btsMZHrDuPz/mymPgILobsLSGc1kSKhiZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A134n/btsMZHrDuPz/mymPgILobsLSGc1kSKhiZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA134n%2FbtsMZHrDuPz%2FmymPgILobsLSGc1kSKhiZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1873&quot; height=&quot;959&quot; data-origin-width=&quot;1873&quot; data-origin-height=&quot;959&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중앙 서치바를 통한 기업 검색을 통해 &lt;span style=&quot;background-color: #ffffff; color: #777777; text-align: start;&quot;&gt;해당 기업의 매출, 이익, 가격 지표 등 다양한 데이터를 확인할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1554&quot; data-origin-height=&quot;811&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/et9MNb/btsMYsoKktP/LstX6DdUdzkufCZkzzCNW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/et9MNb/btsMYsoKktP/LstX6DdUdzkufCZkzzCNW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/et9MNb/btsMYsoKktP/LstX6DdUdzkufCZkzzCNW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fet9MNb%2FbtsMYsoKktP%2FLstX6DdUdzkufCZkzzCNW0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1554&quot; height=&quot;811&quot; data-origin-width=&quot;1554&quot; data-origin-height=&quot;811&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주요 특징:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기업의 과거 15년 치 데이터 제공 (매출, 순이익, 자산, 부채, 현금흐름 등)&lt;/li&gt;
&lt;li&gt;모든 주요 재무 정보를 한눈에 확인 가능&lt;/li&gt;
&lt;li&gt;과거와 현재 비교를 통한 장기 추세 분석 가능&lt;/li&gt;
&lt;li&gt;간편한 데이터 검색 및 비교 기능&lt;/li&gt;
&lt;li&gt;직관적인 그래프와 차트 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) DataRoma&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DataRoma는 기관 투자자들의 포트폴리오 정보를 분석할 수 있는 플랫폼입니다. 운영 자산 규모 1억 달러 이상의 기관 투자자들은 매 분기 말 45일 이내에 증권 포트폴리오 정보를 SEC에 제출해야 하며, DataRoma는 이 데이터를 활용해 투자 대가들의 매매 동향을 추적합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;988&quot; data-origin-height=&quot;806&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTVkpr/btsMYoUl3S3/PUxfv8ik1iBzN8vkLMw4tk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTVkpr/btsMYoUl3S3/PUxfv8ik1iBzN8vkLMw4tk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTVkpr/btsMYoUl3S3/PUxfv8ik1iBzN8vkLMw4tk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTVkpr%2FbtsMYoUl3S3%2FPUxfv8ik1iBzN8vkLMw4tk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;988&quot; height=&quot;806&quot; data-origin-width=&quot;988&quot; data-origin-height=&quot;806&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주요 특징:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 포트폴리오에 반복 등장하는 종목의 신뢰도 평가 가능&lt;/li&gt;
&lt;li&gt;분기별로 업데이트되는 포트폴리오 변화 추적&lt;/li&gt;
&lt;li&gt;투자자들의 전략 분석 가능&lt;/li&gt;
&lt;li&gt;'Top 10 stocks by %' 기능을 통해 중점 투자 종목 파악&lt;/li&gt;
&lt;li&gt;개별 투자자 포트폴리오 세부 분석 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3) Value&amp;nbsp;Investors&amp;nbsp;Club&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Value Investors Club은 실력 있는 가치 투자자들이 모여 투자 아이디어와 심층 기업 분석을 공유하는 커뮤니티입니다. 이 사이트는 기업 분석을 작성하는 회원과 열람만 가능한 회원으로 구분됩니다. 분석 작성 회원은 엄격한 심사를 통과해야 하지만, 열람 회원은 간단한 회원가입만으로도 양질의 기업 분석 자료에 접근할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1288&quot; data-origin-height=&quot;882&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pvHcG/btsMZiMtwzY/Zm7ym4RKGHAbKiSh2voA90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pvHcG/btsMZiMtwzY/Zm7ym4RKGHAbKiSh2voA90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pvHcG/btsMZiMtwzY/Zm7ym4RKGHAbKiSh2voA90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpvHcG%2FbtsMZiMtwzY%2FZm7ym4RKGHAbKiSh2voA90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1288&quot; height=&quot;882&quot; data-origin-width=&quot;1288&quot; data-origin-height=&quot;882&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span&gt;2. &lt;/span&gt;데이터 사이트 및 재무제표 데이터 선정&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 말했듯 유용한 데이터 사이트가 많았지만, 저는 이중에서 macrotrends를 선택했습니다. DataRoma와 Value Investors Club도 유용했지만, 저는 LLM에게 제공해줄 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;객관적인&lt;/b&gt;&lt;/u&gt;&lt;/span&gt; 데이터가 필요했기 때문에, 타인의 의견이 포함되지 않고 오직 데이터만을 객관적으로 표현한 macrotrends를 선택했습니다. 그리고 재무제표에 지표가 너무 많기 때문에 그 중 투자 판단을 내리는데 도움을 줄 수 있는 값들만 따로 선정하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) 대차대조표(Balance Sheet)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Cash On Hand&lt;/b&gt;: 회사가 보유한 현금과 즉시 현금화 가능한 자산&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Long Term Debt&lt;/b&gt;: 1년 이상의 상환 기간을 가진 회사의 장기 부채&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Share Holder Equity&lt;/b&gt;: 주주들이 소유한 회사 가치(자산-부채)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Total Assets&lt;/b&gt;: 회사가 소유한 모든 자산의 총합&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Total Current Assets&lt;/b&gt;: 1년 내에 현금화 가능한 단기 자산&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Total Current Liabilities&lt;/b&gt;: 1년 내에 갚아야 하는 단기 부채&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Total Liabilities&lt;/b&gt;: 회사의 모든 부채 총합&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Total Liabilities And Share Holders Equity&lt;/b&gt;: 부채와 자본의 총합(자산 총계와 일치)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) 현금흐름표(Cash Flow)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Cash Flow From Financial Activities&lt;/b&gt;: 자금조달 활동(대출, 주식발행 등)으로 인한 현금흐름&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Cash Flow From Investing Activities&lt;/b&gt;: 투자 활동(자산 구매/매각)으로 인한 현금흐름&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Cash Flow From Operating Activities&lt;/b&gt;: 영업 활동에서 발생한 현금흐름&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Net Cash Flow&lt;/b&gt;: 모든 현금흐름을 합산한 순 현금흐름&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3) 재무비율(Financial Ratios)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Book Value Per Share&lt;/b&gt;: 주당 순자산 가치(자기자본&amp;divide;발행주식수)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Current Ratio&lt;/b&gt;: 유동비율(유동자산&amp;divide;유동부채), 단기 지급능력 지표&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Debt/Equity Ratio&lt;/b&gt;: 부채비율(총부채&amp;divide;자기자본), 재무 레버리지 지표&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Free Cash Flow Per Share&lt;/b&gt;: 주당 잉여현금흐름, 배당여력 평가 지표&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Gross Margin&lt;/b&gt;: 매출총이익률((매출-매출원가)&amp;divide;매출), 기본 수익성 지표&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Net Profit Margin&lt;/b&gt;: 순이익률(순이익&amp;divide;매출), 최종 수익성 지표&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Operating Margin&lt;/b&gt;: 영업이익률(영업이익&amp;divide;매출), 핵심사업 수익성 지표&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ROA&lt;/b&gt;: 총자산수익률(순이익&amp;divide;총자산), 자산 활용 효율성 지표&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ROE&lt;/b&gt;: 자기자본수익률(순이익&amp;divide;자기자본), 주주투자 수익성 지표&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;4) 손익계산서(Income Statement)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Basic EPS&lt;/b&gt;: 기본 주당순이익(순이익&amp;divide;보통주식수)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;EBIT&lt;/b&gt;: 이자 및 세전이익, 영업성과 평가 지표&lt;/li&gt;
&lt;li&gt;&lt;b&gt;EBITDA&lt;/b&gt;: 이자, 세금, 감가상각비 차감 전 이익, 현금창출능력 지표&lt;/li&gt;
&lt;li&gt;&lt;b&gt;EPS&lt;/b&gt;: 주당순이익, 주주에게 돌아가는 이익 지표&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Gross Profit&lt;/b&gt;: 매출총이익(매출-매출원가)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Net Income&lt;/b&gt;: 당기순이익, 모든 비용 차감 후 최종 이익&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Operating Income&lt;/b&gt;: 영업이익, 본업에서 발생한 이익&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Pre-Tax Income&lt;/b&gt;: 세전이익, 세금 납부 전 이익&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Revenue&lt;/b&gt;: 매출액, 주요 사업활동으로 발생한 총수입&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span&gt;3. 재무제표 추출하기&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선정한 지표들에 대한 값을 crawling으로 가져오도록 하겠습니다. 이 부분에서 고민을 되게 많이했는데, 처음에는 RSS feed 활용법으로 접근했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1557&quot; data-origin-height=&quot;823&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/L3M95/btsMZ50g1eb/7TiSshMhsz2Dnnul6tGt5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/L3M95/btsMZ50g1eb/7TiSshMhsz2Dnnul6tGt5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/L3M95/btsMZ50g1eb/7TiSshMhsz2Dnnul6tGt5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FL3M95%2FbtsMZ50g1eb%2F7TiSshMhsz2Dnnul6tGt5K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1557&quot; height=&quot;823&quot; data-origin-width=&quot;1557&quot; data-origin-height=&quot;823&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원하는 지표를 클릭하고 우측 'View Reports'를 선택하면 보고서 모음 화면으로 이동합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1903&quot; data-origin-height=&quot;949&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDDTmE/btsMYvlCUGR/vee1tH8k9bYEfGwWUYi4Wk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDDTmE/btsMYvlCUGR/vee1tH8k9bYEfGwWUYi4Wk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDDTmE/btsMYvlCUGR/vee1tH8k9bYEfGwWUYi4Wk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDDTmE%2FbtsMYvlCUGR%2Fvee1tH8k9bYEfGwWUYi4Wk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1903&quot; height=&quot;949&quot; data-origin-width=&quot;1903&quot; data-origin-height=&quot;949&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 검색된 보고서들에 대해 RSS Feed를 활용해 XML 파일에 포함된 보고서별 링크를 통해 필요한 데이터만 파싱하려 했습니다. 그러나 이 방법에는 큰 문제가 있었습니다. 기업별로 보고서 형식과 재무제표 구성이 상이했던 것입니다. 동일한 재무지표라도 기업마다 사용하는 태그나 명칭이 달라 일관된 크롤링이 어려웠습니다. 이런 문제를 해결하기 위해 공시 데이터를 구조화된 형태로 제공하는 표준 포맷인 &lt;b&gt;XBRL&lt;/b&gt;을 활용해보려 했습니다. XBRL은 이전보다 통일된 형식을 제공했지만, 여전히 기업마다 차이가 존재했습니다. 결국 모든 기업에서 공통적으로 사용하는 표준 명칭이 필요하다고 판단했습니다. 이에 처음 검색 결과 화면에 표시되는 데이터를 기준으로 크롤링하는 방법을 선택했습니다. 이 방법을 통해 일관된 형식의 재무지표 데이터를 수집할 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Mm5dw9JPBxs&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=Mm5dw9JPBxs&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Activities/FinAgent Lab</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/78</guid>
      <comments>https://striver.tistory.com/entry/FinAgent-Lab-%EC%9E%AC%EB%AC%B4%EC%A0%9C%ED%91%9C-crawling#entry78comment</comments>
      <pubDate>Tue, 25 Mar 2025 23:25:15 +0900</pubDate>
    </item>
    <item>
      <title>MCP (Model Context Protocol)</title>
      <link>https://striver.tistory.com/entry/MCP-Model-Context-Protocol</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. MCP란?&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP는 Model Context protocal의 약자로 2024년 11월에 앤트로픽에서 발표한 개념으로 최근 이슈가 되고 있습니다. 이러한 MCP는 아래와 같이 다양하게 정의되고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- AI 에이전트가 웹 브라우저 없이도 직접 다양한 인터넷 서비스와 소통할 수 있게 해주는 표준 프로토콜&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- AI&amp;nbsp;모델이&amp;nbsp;외부&amp;nbsp;데이터&amp;nbsp;및&amp;nbsp;도구와&amp;nbsp;상호&amp;nbsp;작용할&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;설계된&amp;nbsp;표준&amp;nbsp;프로토콜&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Client(클로드,커서,...)에서 다른 도구들도 쓸 수 있게 통일을 시켜주는 하나의 프로토콜&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 부르는 사람에따라 다양한 의미로 표현되고 있지만, 간단하게 말하면 &quot; Client, LLM, Server를 이어주는 USB-C 포트와 유사한 역할&quot;이라고 할 수 있습니다. 아래 이미지를 참고하면 더 쉽게 이해할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;964&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxo3gg/btsMSV5KQYZ/sse8SyrqB9DcdvwvvsJk01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxo3gg/btsMSV5KQYZ/sse8SyrqB9DcdvwvvsJk01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxo3gg/btsMSV5KQYZ/sse8SyrqB9DcdvwvvsJk01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbxo3gg%2FbtsMSV5KQYZ%2Fsse8SyrqB9DcdvwvvsJk01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1500&quot; height=&quot;964&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;964&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 MCP가 최근 이슈가 되고 있는 이유에 대해서도 생각해 보았습니다. 기존 에이전틱 워크플로우를 구성할 때는, LLM이 중앙에 위치하고, 데이터베이스를 연결하는 별도의 툴을 만든 뒤, LangGraph로 이 툴과 LLM을 연결하는 등의 복잡한 과정이 필요했습니다. 추가 기능이 필요할 때마다 이러한 과정을 반복해야 했죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP의 등장으로 이런 복잡한 과정이 크게 간소화되었습니다. 기존에는 LangChain, LangGraph와 같은 LLM 프레임워크에 통합된 툴을 사용하거나, 직접 툴을 개발한 후 LLM 프레임워크를 통해 에이전트를 구축해야 했습니다. 하지만 MCP를 활용하면 LangChain이나 LangGraph에 대한 전문 지식이 없는 개발자들도 LLM과 원하는 외부 프로그램을 쉽게 연결할 수 있습니다. 이것이 제가 생각하는 MCP의 가장 큰 장점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 MCP가 어떤 방식으로 개발 과정을 간소화하는지 자세히 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. MCP 구성요소&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XsKDZ/btsMTNToNnE/2NBjvRphygubjllVpxRwcK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XsKDZ/btsMTNToNnE/2NBjvRphygubjllVpxRwcK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XsKDZ/btsMTNToNnE/2NBjvRphygubjllVpxRwcK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXsKDZ%2FbtsMTNToNnE%2F2NBjvRphygubjllVpxRwcK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;900&quot; height=&quot;600&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP는 크게 3가지로 구성되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) MCP 클라이언트&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버와 1:1 연결을 유지하는 프로토콜 클라이언트 (Host &amp;amp; Server 간에 연결을 위한 중개자)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) MCP 서버&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;표준화된 모델 컨텍스트 프로토콜을 통해 각각 특정 기능을 노출하는 경량 프로그램&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3) MCP 호스트&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP를 통해 데이터에 액세스하려는 Claude Desktop, IDE 또는 AI 도구와 같은 프로그램&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 설명했던 LangGraph를 활용한 별도의 워크플로우 구축 없이 우리가 LLM에게 주고싶은 툴(DB 조회, 로컬 컴퓨터파일 조작 등) 을 따로 코드를 일일히 작성하고 연결할 필요 없이 이미 잘 만들어져있는 MCP Server를 사용해 LLM에게 지정만해주면 전보다 간단하고 더 다양한 기능을 제공할 수 있습니다. 기존 API 방식과 비교하면 아래와 같은 차이를 지니고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;841&quot; data-origin-height=&quot;235&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Cnzaq/btsMT0SN6Ym/idUyNvvrEQatB23xcTaf6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Cnzaq/btsMT0SN6Ym/idUyNvvrEQatB23xcTaf6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Cnzaq/btsMT0SN6Ym/idUyNvvrEQatB23xcTaf6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCnzaq%2FbtsMT0SN6Ym%2FidUyNvvrEQatB23xcTaf6k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;841&quot; height=&quot;235&quot; data-origin-width=&quot;841&quot; data-origin-height=&quot;235&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 이러한 MCP는 어디서 어떻게 사용해야할까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. MCP 사용방법&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 간단하게 우리가 사용할 수 있는 방법은 클로드 프로를 결제하고 사용하는 방법입니다. 클로드 프로를 구매하고 클로드 앱을 다운로드 받은 후 &quot;Claude_desktop_config.json&quot; 파일의 구성을 변경해주면 됩니다. 먼저 파일-&amp;gt;설정을 들어가줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;391&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/26mVU/btsMSa3D1Ca/NaYexcvh6e3E2ViaLtg3kK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/26mVU/btsMSa3D1Ca/NaYexcvh6e3E2ViaLtg3kK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/26mVU/btsMSa3D1Ca/NaYexcvh6e3E2ViaLtg3kK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F26mVU%2FbtsMSa3D1Ca%2FNaYexcvh6e3E2ViaLtg3kK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;590&quot; height=&quot;391&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;391&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무것도 설정하지 않은 처음에는 아래와 같은 화면이 나올 것 입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;767&quot; data-origin-height=&quot;528&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XVE7S/btsMUiSUxLu/nBp1hjsLMlcCz04uVp3P01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XVE7S/btsMUiSUxLu/nBp1hjsLMlcCz04uVp3P01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XVE7S/btsMUiSUxLu/nBp1hjsLMlcCz04uVp3P01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXVE7S%2FbtsMUiSUxLu%2FnBp1hjsLMlcCz04uVp3P01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;767&quot; height=&quot;528&quot; data-origin-width=&quot;767&quot; data-origin-height=&quot;528&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 시작하기를 누르면 MCP 공식문서로 넘어갑니다. 그리고 설정 편집을 누르면 클로드가 설치되어있는 폴더로 넘어오게 되는데, 여기서 &quot; Claude_desktop_config.json&quot; 파일을 수정해주면 됩니다. 파일을 클릭해보면 비어있는 json 파일이 열릴텐데 여기서 내가 사용하고 싶은 mcpServer를 아래 예시처럼 추가하고 저장해주면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742629336735&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;mcpServers&quot;: {
    &quot;filesystem&quot;: {
      &quot;command&quot;: &quot;npx&quot;,
      &quot;args&quot;: [
        &quot;-y&quot;,
        &quot;@modelcontextprotocol/server-filesystem&quot;,
        &quot;/Users/username/Desktop&quot;,
        &quot;/path/to/other/allowed/dir&quot;
      ]
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 후, 클로드 앱을 껏다가 키면 해당 mcpServer를 클로드가 인지하고 사용할 수 있게됩니다. 그렇다면 이러한 MCP는 어디서 사용할 수 있을까요? 물론 직접 만들 수 있습니다. 하지만 이미 잘 만들어져있는 MCP가 많기때문에 이것들을 활용하는 것만으로도 충분할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) MCP Server Directory&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1572&quot; data-origin-height=&quot;846&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/POpYu/btsMS8DLktM/8sighwLft7Hf2NMYYJUSlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/POpYu/btsMS8DLktM/8sighwLft7Hf2NMYYJUSlK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/POpYu/btsMS8DLktM/8sighwLft7Hf2NMYYJUSlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPOpYu%2FbtsMS8DLktM%2F8sighwLft7Hf2NMYYJUSlK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1572&quot; height=&quot;846&quot; data-origin-width=&quot;1572&quot; data-origin-height=&quot;846&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 기능도 있고, 우리가 사용하고싶은 MCP Server들을 좀 더 직관적으로 보고 선택할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) Awesome MCP Servers&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;832&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEmB4b/btsMUxh9pej/j67pFfjWk4em3LKkzDkLDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEmB4b/btsMUxh9pej/j67pFfjWk4em3LKkzDkLDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEmB4b/btsMUxh9pej/j67pFfjWk4em3LKkzDkLDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEmB4b%2FbtsMUxh9pej%2Fj67pFfjWk4em3LKkzDkLDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;926&quot; height=&quot;832&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;832&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;github 레파지토리형태로 제공되기때문에 MCP Server Direcotry보다는 직관적이지 않을 수 있지만 마찬가지로 유용하기 때문에 본인에게 맞는 방법을 사용해서 자신에게 맞는 MCP 서버를 찾으면 되겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) smithery&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1875&quot; data-origin-height=&quot;962&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjAIPs/btsM2qcyimj/QpaGnVJL9prSJVKvJQoto0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjAIPs/btsM2qcyimj/QpaGnVJL9prSJVKvJQoto0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjAIPs/btsM2qcyimj/QpaGnVJL9prSJVKvJQoto0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjAIPs%2FbtsM2qcyimj%2FQpaGnVJL9prSJVKvJQoto0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1875&quot; height=&quot;962&quot; data-origin-width=&quot;1875&quot; data-origin-height=&quot;962&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4. MCP 장단점&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 기존 AI 에이전트와 API 방식 대비 이점에 대해 간단하게 설명했지만, MCP를 사용했을때의 장단점에 대해 조금 더 명확히 작성해보고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1) 장점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;외부 도구 연결 가능&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- AI 모델이 원래 지원하지 않는 새로운 도구를 쉽게 추가할 수 있습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;유연성과 확장성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- AI 모델이 다양한 데이터 소스와 유연하게 연결이 가능하고 확장성이 뛰어납니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;양방향 통신과 간편한 유지보수&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 실시간 상호작용이 가능하여 반응 속도가 향상되며 상대적으로 유지보수가 간편합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) 단점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AI 에이전트와 도구 통합 문제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 모델에 기존에 설계된 내용이 있기 때문에 도구를 추가하는 것만으로 AI가 제대로 작동하는 것은 아니며, 추가시 전체적인 시스템 구조와 조정이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정확도와 신뢰성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 새로운 도구를 추가하는 것은 쉽지만, 그 도구를 얼마나 정확하게 사용할 수 있을지는 보장할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;보안 및 데이터 관리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 서버 기반으로 동작하려면 사용자 인증 및 보안 문제를 해결해야하고, 데이터 유출이나 악성 도구 연결 등의 보안 관리가 면밀히 검토 될 필요가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로, MCP가 공개된 지 3개월 정도 지난 지금에서야 주목받게 된 이유는 Cursor AI에서 2월에 발표한 MCP 업데이트와 밀접한 연관이 있다는 테디노트님의 의견이 있었습니다. 생각해보면 Cursor AI의 도입이 없었다면 MCP가 지금처럼 인기를 얻지 못했을 수도 있었을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 기술이 주목받게 된 배경을 살펴보니, 아무리 혁신적인 기술을 개발하더라도 사람들이 쉽게 접근하고 활용할 수 있는 환경을 만드는 것이 얼마나 중요한지 다시금 생각하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=0sUN3d4atoc&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=0sUN3d4atoc&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://vision-ai.tistory.com/235&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://vision-ai.tistory.com/235&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://digitalbourgeois.tistory.com/875&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://digitalbourgeois.tistory.com/875&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=VKIl0TIDKQg&amp;amp;t=27s&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=VKIl0TIDKQg&amp;amp;t=27s&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=ISrYHGg2C2c&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=ISrYHGg2C2c&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Agent</category>
      <category>Claude</category>
      <category>cursor ai</category>
      <category>MCP</category>
      <category>mcp client</category>
      <category>mcp server</category>
      <category>model context protocol</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/77</guid>
      <comments>https://striver.tistory.com/entry/MCP-Model-Context-Protocol#entry77comment</comments>
      <pubDate>Sat, 22 Mar 2025 16:52:14 +0900</pubDate>
    </item>
    <item>
      <title>[Github 잔디 심기]티스토리 블로그 + Github 연동</title>
      <link>https://striver.tistory.com/entry/%ED%8B%B0%EC%8A%A4%ED%86%A0%EB%A6%AC-%EB%B8%94%EB%A1%9C%EA%B7%B8-Github-%EC%97%B0%EB%8F%99-%EC%9E%94%EB%94%94-%EC%8B%AC%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;티스토리 블로그에 글을 작성하면 깃허브 계정에서 자동으로 잔디가 심어지도록 설정해보도록 하겠습니다. 그러기 위해 Github Action을 이용하여 하루에 한번 티스토리의 RSS를 기반으로 Github에 커밋이 되도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 티스토리 RSS 설정&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 티스토리 블로그에서 RSS 를 설정해줍니다. 내 블로그 설정 -&amp;gt; 관리 -&amp;gt; 블로그 -&amp;gt; 기타 설정에 위치한 RSS를 &quot;전체 공개&quot;로 설정 후 저장해줍니다. &quot;&lt;a href=&quot;https://striver.tistory.com/rss&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://striver.tistory.com/rss&lt;/a&gt;&quot; 와 같이 자신의 블로그 주소에 접속하면 RSS 정보가 확인 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1247&quot; data-origin-height=&quot;448&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IMSLn/btsMRdeV4UX/3rkJiPnyIJZWkyy1tpdW2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IMSLn/btsMRdeV4UX/3rkJiPnyIJZWkyy1tpdW2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IMSLn/btsMRdeV4UX/3rkJiPnyIJZWkyy1tpdW2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIMSLn%2FbtsMRdeV4UX%2F3rkJiPnyIJZWkyy1tpdW2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1247&quot; height=&quot;448&quot; data-origin-width=&quot;1247&quot; data-origin-height=&quot;448&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. Repository 생성&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Github Action으로 연동할 새로운 repository를 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1888&quot; data-origin-height=&quot;945&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ddCayJ/btsMSPXWVWV/yGLpVZZJH0Z5kdKTqBlW6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ddCayJ/btsMSPXWVWV/yGLpVZZJH0Z5kdKTqBlW6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ddCayJ/btsMSPXWVWV/yGLpVZZJH0Z5kdKTqBlW6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FddCayJ%2FbtsMSPXWVWV%2FyGLpVZZJH0Z5kdKTqBlW6k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1888&quot; height=&quot;945&quot; data-origin-width=&quot;1888&quot; data-origin-height=&quot;945&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 로컬 환경과 Repository 연동&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자신이 편한 위치에 폴더를 하나 만들고 vscode로 들어간 후 해당 폴더를 이전에 만들었던 Repository와 연동해주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742536544352&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git init
git branch -M main
git remote add origin https://github.com/ehdtjr/ehdtjr.git&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연동이 잘 되었는지는 &quot;git remote -v&quot; 로 확인해 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;653&quot; data-origin-height=&quot;200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cysCwG/btsMSlwfVA0/xxW0KhJMwTliun0QX4H1zK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cysCwG/btsMSlwfVA0/xxW0KhJMwTliun0QX4H1zK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cysCwG/btsMSlwfVA0/xxW0KhJMwTliun0QX4H1zK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcysCwG%2FbtsMSlwfVA0%2FxxW0KhJMwTliun0QX4H1zK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;653&quot; height=&quot;200&quot; data-origin-width=&quot;653&quot; data-origin-height=&quot;200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4. RSS 및 Git Action 파일 작성&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;package.json 파일을 생성하기 위해 아래 명령어를 입력합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742538384954&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm init -y&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 npm이 설치되지 않았을 경우 &quot;brew install node&quot; 명령어를 선행으로 실행하여 설치해줍니다. (mac os 기준)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;이어서 RSS 정보를 파싱할 수 있는 rss-parser 라이브러리를 설치합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742538445465&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm i rss-parser&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;생성된 package.json 파일을 열어서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&quot;type&quot;: &quot;module&quot;,&lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&quot;start&quot;:&amp;nbsp;&quot;node&amp;nbsp;index.js&quot;,&lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;를 추가합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742538535344&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;github_tistory&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;main&quot;: &quot;index.js&quot;,
  &quot;type&quot;: &quot;module&quot;,
  &quot;scripts&quot;: {
    &quot;start&quot;: &quot;node index.js&quot;,
    &quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;amp;&amp;amp; exit 1&quot;
  },
  &quot;repository&quot;: {
    &quot;type&quot;: &quot;git&quot;,
    &quot;url&quot;: &quot;git+https://github.com/ehdtjr/ehdtjr.git&quot;
  },
  &quot;keywords&quot;: [],
  &quot;author&quot;: &quot;&quot;,
  &quot;license&quot;: &quot;ISC&quot;,
  &quot;bugs&quot;: {
    &quot;url&quot;: &quot;https://github.com/ehdtjr/ehdtjr/issues&quot;
  },
  &quot;homepage&quot;: &quot;https://github.com/ehdtjr/ehdtjr#readme&quot;,
  &quot;description&quot;: &quot;&quot;,
  &quot;dependencies&quot;: {
    &quot;rss-parser&quot;: &quot;^3.13.0&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 프로젝트의 root에 index.js 파일을 새롭게 만들고 아래와같이 작성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742538596155&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { writeFileSync } from 'node:fs';
import Parser from &quot;rss-parser&quot;;

/**
 * README.MD에 작성될 페이지 텍스트
 * @type {string}
 */
let text = `# Hi there  

## 이런 환경에 익숙해요✍ 

## 언어

&amp;lt;p&amp;gt;
  &amp;lt;img alt=&quot;&quot; src= &quot;https://img.shields.io/badge/python-3670A0?style=for-the-badge&amp;amp;logo=python&amp;amp;logoColor=ffdd54&quot;/&amp;gt; 
&amp;lt;/p&amp;gt;

##   Latest Blog Posts

`;

// rss-parser 생성
const parser = new Parser({
    headers: {
        Accept: 'application/rss+xml, application/xml, text/xml; q=0.1',
    }});

(async () =&amp;gt; {

    // 피드 목록
    const feed = await parser.parseURL('https://striver.tistory.com/rss'); // 본인의 블로그 주소
    
    text += `&amp;lt;ul&amp;gt;`;
    
    // 최신 10개의 글의 제목과 링크를 가져온 후 text에 추가
    for (let i = 0; i &amp;lt; 10; i++) {
        const {title, link} = feed.items[i];
        console.log(`${i + 1}번째 게시물`);
        console.log(`추가될 제목: ${title}`);
        console.log(`추가될 링크: ${link}`);
        text += `&amp;lt;li&amp;gt;&amp;lt;a href='${link}' target='_blank'&amp;gt;${title}&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;`;
    }

    text += `&amp;lt;/ul&amp;gt;`;
    
    // README.md 파일 생성
    writeFileSync('README.md', text, 'utf8', (e) =&amp;gt; {
        console.log(e);
    })
    console.log('업데이트 완료');
})();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;5. main.yml 생성&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Git action 파일을 생성하기 위해 프로젝트 루트에서 &lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;.github\workflows 폴더를 생성하고 해당 폴더 안에 main.yml을 생성합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742538654694&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# This is a basic workflow to help you get started with Actions

name: Readme Update

# Controls when the workflow will run
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
  # 1시간에 한번씩 아래 스크립트를 실행한다.
  schedule:
    - cron: &quot;0 */1 * * *&quot;
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called &quot;build&quot;
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: 20

      - name: Install dependencies
        run: | 
          npm ci
          npm install rss-parser

      - name: Update README
        run: npm start

      - name: Check if there are any changes
        id: verify_diff
        run: |
          git diff --quiet . || echo &quot;changed=true&quot; &amp;gt;&amp;gt; $GITHUB_OUTPUT

      - name: Commit README
        if: steps.verify_diff.outputs.changed == 'true'
        run: |
          git config --local user.email &quot;dskang207@gmail.com&quot;
          git config --local user.name &quot;ehdtjr&quot;
          git add .
          git commit -m &quot;Update README.md&quot;
          git push origin main&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제일 하단의 run 부분을 자신의 github email, name으로 변경해줘야하고 cron으로 스크립트 실행 주기를 설정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;6. Git action permission 설정&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Github Action 과정에서 발생할 403 permission 오류를 제어하기 위해 설정을 해줘야합니다. Settings -&amp;gt; Actions -&amp;gt; General에 들어가 하단 설정을 바꾸고 Save를 눌러줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1178&quot; data-origin-height=&quot;938&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WLZdc/btsMRV5NfZ2/y28DxDi7tIKtM1gWQuQaj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WLZdc/btsMRV5NfZ2/y28DxDi7tIKtM1gWQuQaj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WLZdc/btsMRV5NfZ2/y28DxDi7tIKtM1gWQuQaj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWLZdc%2FbtsMRV5NfZ2%2Fy28DxDi7tIKtM1gWQuQaj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1178&quot; height=&quot;938&quot; data-origin-width=&quot;1178&quot; data-origin-height=&quot;938&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/554&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://peterica.tistory.com/554&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leejaehoon.tistory.com/entry/Github-Tistory-Github-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://leejaehoon.tistory.com/entry/Github-Tistory-Github-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Dev/Git&amp;amp;GitHub</category>
      <category>git</category>
      <category>github</category>
      <category>github actions</category>
      <category>잔디심기</category>
      <category>티스토리 블로그 연동</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/76</guid>
      <comments>https://striver.tistory.com/entry/%ED%8B%B0%EC%8A%A4%ED%86%A0%EB%A6%AC-%EB%B8%94%EB%A1%9C%EA%B7%B8-Github-%EC%97%B0%EB%8F%99-%EC%9E%94%EB%94%94-%EC%8B%AC%EA%B8%B0#entry76comment</comments>
      <pubDate>Fri, 21 Mar 2025 15:35:37 +0900</pubDate>
    </item>
    <item>
      <title>[LLMops] 오픈소스 LLM 평가 프레임워크 opik</title>
      <link>https://striver.tistory.com/entry/LLMops-%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-LLM-%ED%8F%89%EA%B0%80-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-opik</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 오픈소스로 제공되는 LLM 평가 프레임워크인 opik을 처음 접하게 되어 간단한 설명과 활용방법에 대해 얘기해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;opik은 &lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;LLM 애플리케이션을 평가, 테스트 및 모니터링하기 위한 오픈소스 플랫폼입니다. 저는 보통 RAG 파이프라인을 구성한후 추적을 하는 용도로 Langsmith를 많이 사용했는데, opik에서 좀 더 직관적인 ui와 &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;LLM 성능을 자동으로 평가하는 기능도&amp;nbsp;&lt;/span&gt;제공한다고해서 사용해보게 되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2550&quot; data-origin-height=&quot;1228&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjyBah/btsMHSBKVlv/0N9kgYeyvhF5ASDmnLgCO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjyBah/btsMHSBKVlv/0N9kgYeyvhF5ASDmnLgCO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjyBah/btsMHSBKVlv/0N9kgYeyvhF5ASDmnLgCO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjyBah%2FbtsMHSBKVlv%2F0N9kgYeyvhF5ASDmnLgCO1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2550&quot; height=&quot;1228&quot; data-origin-width=&quot;2550&quot; data-origin-height=&quot;1228&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인하고 볼 수 있는 메인 화면인데 상당히 깔끔한 구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2545&quot; data-origin-height=&quot;1231&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/R5KcC/btsMIwLverU/CDMHRYCxDkiXiORHkiTYhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/R5KcC/btsMIwLverU/CDMHRYCxDkiXiORHkiTYhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/R5KcC/btsMIwLverU/CDMHRYCxDkiXiORHkiTYhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FR5KcC%2FbtsMIwLverU%2FCDMHRYCxDkiXiORHkiTYhK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2545&quot; height=&quot;1231&quot; data-origin-width=&quot;2545&quot; data-origin-height=&quot;1231&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하단의 Quickstart guide를 보면 사용하는 프레임워크별로 LLM을 추적할 수 있게끔 샘플 코드가 작성되어있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741773565743&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langchain_openai import ChatOpenAI
from common.state_graph import GraphState
from retrieve_docs import retrieve_docs, summary_docs, evaluate_docs
from langgraph.graph import StateGraph, START, END
from opik.integrations.langchain import OpikTracer

# 모델 설정
model = ChatOpenAI(model_name=&quot;gpt-4o-mini&quot;)

# 워크플로우 생성 및 노드 등록
workflow = StateGraph(GraphState)

workflow.add_node(&quot;retrieve_docs&quot;, retrieve_docs)
workflow.add_node(&quot;summary_docs&quot;, summary_docs)
workflow.add_node(&quot;evaluate_docs&quot;, evaluate_docs)

##### 엣지 정의 예시 #####
workflow.add_edge(START, &quot;retrieve_docs&quot;)
workflow.add_edge(&quot;retrieve_docs&quot;, &quot;summary_docs&quot;)
workflow.add_edge(&quot;summary_docs&quot;, &quot;evaluate_docs&quot;)
# workflow.add_edge(&quot;evaluate_docs&quot;, &quot;retrieve_docs&quot;)
workflow.add_edge(&quot;evaluate_docs&quot;, END)

app = workflow.compile()

tracer = OpikTracer(graph=app.get_graph(xray=True))
inputs = {&quot;question&quot;: &quot;안녕하세요&quot;}
result = app.invoke(inputs, config={&quot;callbacks&quot;: [tracer]})

print(result)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 사용하고 있는 프레임워크는 LangGraph여서 관련 샘플 코드를 참고해서 제 코드에 추가하였습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741773908569&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;OPIK_API_KEY=Md3KFb....
OPIK_WORKSPACE=retrieve_docs&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 저는 Opik api key와 project명에 대한 설정을 .env 파일에 작성해주고 코드를 실행하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741773759841&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;OPIK: Failed to process CreateSpansBatchMessage. Error: status_code: 400, body: {'code': 400, 'message': 'No such workspace: retrieve docs'}
OPIK: Failed to process CreateTraceBatchMessage. Error: status_code: 400, body: {'code': 400, 'message': 'No such workspace: retrieve docs'}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 분명 가이드라인을 따라 코드를 작성했고, 별도의 커스텀도 하지않았는데 위 에러가 계속 발생하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2541&quot; data-origin-height=&quot;593&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8sHei/btsMJMNa7dg/rNHb57bGHtxH79l3kdJji1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8sHei/btsMJMNa7dg/rNHb57bGHtxH79l3kdJji1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8sHei/btsMJMNa7dg/rNHb57bGHtxH79l3kdJji1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8sHei%2FbtsMJMNa7dg%2FrNHb57bGHtxH79l3kdJji1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2541&quot; height=&quot;593&quot; data-origin-width=&quot;2541&quot; data-origin-height=&quot;593&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 원하는 프로젝트명도 분명 만들어둔 상태였는데 에러가 계속 발생해서 원인을 찾지 못하고 있었습니다. 그러다 문득 retrieve docs를 만들어둔 Projects가 workspace와 다른게 아닐까? 라는 의문이 들었고 OpikTracer 클래스 내부를 살펴보니 매개변수로 project_name 값을 선택적 Input으로 받는걸 확인했습니다. 그래서 .env의 OPIK_WORKSPACE를 지우고 새롭게 아래와 같이 코드를 수정하고 다시 실행해 보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741774149672&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tracer = OpikTracer(graph=app.get_graph(xray=True), project_name=&quot;retrieve_docs&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2546&quot; data-origin-height=&quot;1220&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Mqs9d/btsMHtB8Qh3/puhGhCSquyJp4Yb0mKtD81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Mqs9d/btsMHtB8Qh3/puhGhCSquyJp4Yb0mKtD81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Mqs9d/btsMHtB8Qh3/puhGhCSquyJp4Yb0mKtD81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMqs9d%2FbtsMHtB8Qh3%2FpuhGhCSquyJp4Yb0mKtD81%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2546&quot; height=&quot;1220&quot; data-origin-width=&quot;2546&quot; data-origin-height=&quot;1220&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 실행후, 화면을 확인해보니 성공적으로 추적이 되고 있는 모습을 볼 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1903&quot; data-origin-height=&quot;789&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yl4Rs/btsMIqYQT5e/aDeAdlfxKg6p1Cx2rw0xq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yl4Rs/btsMIqYQT5e/aDeAdlfxKg6p1Cx2rw0xq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yl4Rs/btsMIqYQT5e/aDeAdlfxKg6p1Cx2rw0xq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fyl4Rs%2FbtsMIqYQT5e%2FaDeAdlfxKg6p1Cx2rw0xq1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1903&quot; height=&quot;789&quot; data-origin-width=&quot;1903&quot; data-origin-height=&quot;789&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추적한 내용을 살펴보면, 각 노드에서 소요되는 시간과, 노드별 In/Output 결과를 직관적으로 볼 수 있었습니다. 더 사용해봐야겠지만 상당히 편리하고 유용하다는 느낌을 받았습니다. 이제 평가에 대한 작업도 진행해보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고자료&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/comet-ml/opik?tab=readme-ov-file&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/comet-ml/opik?tab=readme-ov-file&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>LLM Engineering/LLMops</category>
      <category>Comet</category>
      <category>createspansbatchmessage</category>
      <category>langgraph</category>
      <category>llm평가</category>
      <category>opik</category>
      <category>평가</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/75</guid>
      <comments>https://striver.tistory.com/entry/LLMops-%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-LLM-%ED%8F%89%EA%B0%80-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-opik#entry75comment</comments>
      <pubDate>Wed, 12 Mar 2025 19:15:48 +0900</pubDate>
    </item>
    <item>
      <title>[Fine-Tuning] LLM 파인튜닝 솔루션 - Unsloth</title>
      <link>https://striver.tistory.com/entry/Fine-Tuning-LLM-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D-%EC%86%94%EB%A3%A8%EC%85%98-Unsloth</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;LLM을 클라우드 환경이나 Google Colab에서 파인튜닝하는 과정에서 종종 라이브러리 간 의존성 충돌로 인해 실행 오류가 발생하고, 높은 메모리 사용량과 긴 학습 시간이 문제가 되곤 합니다. 이러한 문제를 해결할 수 있는 보다 효율적인 방법을 찾던 중, 단일 GPU 환경에서도 최적의 성능을 제공하는 &quot;Unsloth&quot;를 접하게 되어 소개해 보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Unsloth란 무엇인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unsloth는 LLM(대형 언어 모델) 파인튜닝을 보다 효율적으로 수행할 수 있도록 설계된 혁신적인 도구입니다. Michael과 Daniel Han 형제가 개발한 이 프로젝트는 적은 자원으로도 강력한 성능을 발휘할 수 있도록 최적화되어 있으며, 학습 속도 향상과 메모리 사용량 절감을 주요 목표로 하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unsloth는 NVIDIA의 GTX 1070과 같은 저사양 GPU부터 최신 H100까지 폭넓은 하드웨어를 지원하며, 허깅페이스(Hugging Face) 생태계와 완벽히 호환됩니다. 이를 통해 연구자와 개발자들은 기존 환경에서 쉽게 활용할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.1 Unsloth의 주요 특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;고속 학습&lt;/b&gt;: 기존 기법보다 빠른 학습 속도를 제공하며, 대부분의 GPU 환경에서 뛰어난 성능을 발휘합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;메모리 최적화&lt;/b&gt;: 대형 모델을 단일 GPU에서도 학습할 수 있도록 메모리 사용량을 최소화합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;다양한 호환성&lt;/b&gt;: 허깅페이스의 SFTTrainer, DPOTrainer, PPOTrainer 등과 연동되어 익숙한 환경에서 활용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Unsloth의 최적화 기술&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unsloth는 다양한 최적화 기술을 활용하여 학습 속도를 향상시키고 메모리 사용량을 줄일 수 있도록 설계되었습니다. 대표적인 기술들을 살펴보겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 Intelligent Weight Upcasting (지능형 가중치 업캐스팅)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 QLoRA(양자화된 Low-Rank Adaptation) 기법에서는 모델 안정성을 위해 일부 계층을 FP32로 변환합니다. Unsloth는 특정 모델(Mistral, LLaMA 등)에 최적화된 방식으로 업캐스팅을 수행하여 메모리 및 연산 효율성을 극대화합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 Manual Autograd (수동 그래디언트 계산)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pytorch의 자동 미분 시스템(AutoGrad)은 LoRA 기반 파인튜닝 과정에서 비효율적일 수 있습니다. Unsloth는 자동 미분을 수동으로 최적화하여 연산 비용을 줄이고 속도를 높였습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.3 Triton 커널 최적화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unsloth는 OpenAI에서 개발한 Triton을 활용하여 GPU 연산 성능을 극대화하였습니다. 이를 통해 연산 코드의 핵심 부분을 최적화하여 높은 학습 성능을 제공합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.4 xFormers 프레임워크 활용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unsloth는 xFormers 프레임워크와 Flash-Attention을 활용하여 메모리 사용량을 절감하고 연산 속도를 가속화합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Unsloth를 활용한 LLM 파인튜닝&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unsloth를 사용하면 복잡한 최적화 작업 없이 간편하게 LLM을 파인튜닝할 수 있습니다. 아래는 &lt;b&gt;Gemma-2-9b 모델&lt;/b&gt;을 Google Colab에서 파인튜닝하는 예제입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 Unsloth 설치&lt;/h3&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;!pip install &quot;unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git&quot;
!pip install --no-deps &quot;xformers&amp;lt;0.0.27&quot; &quot;trl&amp;lt;0.9.0&quot; peft accelerate bitsandbytes

# %%capture
# !pip install unsloth
# # Also get the latest nightly Unsloth!
# !pip uninstall unsloth -y &amp;amp;&amp;amp; pip install --upgrade --no-cache-dir --no-deps git+https://github.com/unslothai/unsloth.git

# # Install Flash Attention 2 for softcapping support
# import torch
# if torch.cuda.get_device_capability()[0] &amp;gt;= 8:
# !pip install --no-deps packaging ninja einops &quot;flash-attn&amp;gt;=2.6.3&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 FastLanguageModel을 이용한 모델 로딩&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unsloth 라이브러리에서 FastLanguageModel 클래스를 import 합니다. FastLanguageModel 클래스는 Unsloth에서 가장 중요한 클래스 입니다. 허깅페이스 Transformers의 다양한 라이브러리들을 기본으로 하여 추가적인 최적화 및 패치 작업을 진행하게 됩니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from unsloth import FastLanguageModel
import torch

max_seq_length = 2048
dtype = None
load_in_4bit = True

model, tokenizer = FastLanguageModel.from_pretrained(
    model_name=&quot;unsloth/gemma-2-9b&quot;,
    max_seq_length=max_seq_length,
    dtype=dtype,
    load_in_4bit=load_in_4bit,
)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.3 PEFT 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastLanguageModel 클래스의 get_peft_model 메서드는 주어진 모델과 설정을 기반으로 PEFT 객체를 반환합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from trl import SFTTrainer
from transformers import TrainingArguments
from unsloth import is_bfloat16_supported

model = FastLanguageModel.get_peft_model(
    model,
    r=16,
    target_modules=[&quot;q_proj&quot;, &quot;k_proj&quot;, &quot;v_proj&quot;, &quot;o_proj&quot;, &quot;gate_proj&quot;, &quot;up_proj&quot;, &quot;down_proj&quot;],
    lora_alpha=16,
    lora_dropout=0,
    bias=&quot;none&quot;,
    use_gradient_checkpointing=&quot;unsloth&quot;,
    random_state=3407,
    use_rslora=False,
    loftq_config=None,
)

trainer = SFTTrainer(
    model=model,
    tokenizer=tokenizer,
    train_dataset=dataset,
    dataset_text_field=&quot;text&quot;,
    max_seq_length=max_seq_length,
    dataset_num_proc=2,
    packing=False,
    args=TrainingArguments(
        per_device_train_batch_size=2,
        gradient_accumulation_steps=4,
        warmup_steps=5,
        max_steps=60,
        learning_rate=2e-4,
        fp16=not is_bfloat16_supported(),
        bf16=is_bfloat16_supported(),
        logging_steps=1,
        optim=&quot;adamw_8bit&quot;,
        weight_decay=0.01,
        lr_scheduler_type=&quot;linear&quot;,
        seed=3407,
        output_dir=&quot;outputs&quot;,
    ),
)

trainer.train()&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.4 Data Prep&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Alpaca dataset으로 샘플 데이터셋을 구성해줍니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;alpaca_prompt = &quot;&quot;&quot;Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request.

### Instruction:
{}


### Input:
{}


### Response:
{}&quot;&quot;&quot;



EOS_TOKEN = tokenizer.eos_token # Must add EOS_TOKEN
def formatting_prompts_func(examples):
instructions = examples[&quot;instruction&quot;]
inputs = examples[&quot;input&quot;]
outputs = examples[&quot;output&quot;]
texts = []
for instruction, input, output in zip(instructions, inputs, outputs):
# Must add EOS_TOKEN, otherwise your generation will go on forever!
text = alpaca_prompt.format(instruction, input, output) + EOS_TOKEN
texts.append(text)
return { &quot;text&quot; : texts, }
pass

from datasets import load_dataset
dataset = load_dataset(&quot;yahma/alpaca-cleaned&quot;, split = &quot;train&quot;)
dataset = dataset.map(formatting_prompts_func, batched = True,)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.5 Train the model&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unsloth 모델은 SFTTrainer을 그대로 사용할 수 있으므로 별도의 랩핑된 학습 라이브러리가 필요하지 않습니다. SFTTrainer를 통해 기존에 사용하던 방식대로 모델, 토크나이저, 데이터셋 및 학습과 관련된 파라미터들을 설정하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;from trl import SFTTrainer
from transformers import TrainingArguments
from unsloth import is_bfloat16_supported

trainer = SFTTrainer(
model = model,
tokenizer = tokenizer,
train_dataset = dataset,
dataset_text_field = &quot;text&quot;,
max_seq_length = max_seq_length,
dataset_num_proc = 2,
packing = False, # Can make training 5x faster for short sequences.
args = TrainingArguments(
per_device_train_batch_size = 2,
gradient_accumulation_steps = 4,
warmup_steps = 5,
max_steps = 60,
learning_rate = 2e-4,
fp16 = not is_bfloat16_supported(),
bf16 = is_bfloat16_supported(),
logging_steps = 1,
optim = &quot;adamw_8bit&quot;,
weight_decay = 0.01,
lr_scheduler_type = &quot;linear&quot;,
seed = 3407,
output_dir = &quot;outputs&quot;,
report_to = &quot;none&quot;, # Use this for WandB etc
),

)

trainer_stats = trainer.train()
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;QLoRA를 통해 학습될 파라미터 수의 비율을 확인할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;model.print_trainable_parameters()

# trainable params: 54,018,048 || all params: 10,213,228,032 || trainable%: 0.5289&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Show current memory stats&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 사용하고 있는 GPU의 타입과 메모리에 대한 정보를 얻을 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;#@title Show current memory stats
gpu_stats = torch.cuda.get_device_properties(0)
start_gpu_memory = round(torch.cuda.max_memory_reserved() / 1024 / 1024 / 1024, 3)
max_memory = round(gpu_stats.total_memory / 1024 / 1024 / 1024, 3)
print(f&quot;GPU = {gpu_stats.name}. Max memory = {max_memory} GB.&quot;)
print(f&quot;{start_gpu_memory} GB of memory reserved.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Show final memory and time stats&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학습 시간, 사용 메모리 등 파인 튜닝 과정에 대한 전반적인 정보들을 알 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;#@title Show final memory and time stats
used_memory = round(torch.cuda.max_memory_reserved() / 1024 / 1024 / 1024, 3)
used_memory_for_lora = round(used_memory - start_gpu_memory, 3)
used_percentage = round(used_memory /max_memory*100, 3)
lora_percentage = round(used_memory_for_lora/max_memory*100, 3)
print(f&quot;{trainer_stats.metrics['train_runtime']} seconds used for training.&quot;)
print(f&quot;{round(trainer_stats.metrics['train_runtime']/60, 2)} minutes used for training.&quot;)
print(f&quot;Peak reserved memory = {used_memory} GB.&quot;)
print(f&quot;Peak reserved memory for training = {used_memory_for_lora} GB.&quot;)
print(f&quot;Peak reserved memory % of max memory = {used_percentage} %.&quot;)
print(f&quot;Peak reserved memory for training % of max memory = {lora_percentage} %.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unsloth는 LLM 파인튜닝을 위한 최적화된 솔루션으로, 적은 자원으로도 강력한 성능을 제공합니다. 다양한 최적화 기술을 통해 속도와 메모리 효율을 극대화하며, 허깅페이스 생태계와의 높은 호환성 덕분에 손쉽게 적용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM 파인튜닝을 보다 빠르고 효율적으로 수행하고 싶다면, Unsloth를 적극 활용해보는 것을 추천합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://devocean.sk.com/blog/techBoardDetail.do?ID=166285&amp;amp;boardType=techBlog&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://devocean.sk.com/blog/techBoardDetail.do?ID=166285&amp;amp;boardType=techBlog&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://digitalbourgeois.tistory.com/433&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://digitalbourgeois.tistory.com/433&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://colab.research.google.com/drive/1vIrqH5uYDQwsJ4-OO3DErvuv4pBgVwk4?usp=sharing&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://colab.research.google.com/drive/1vIrqH5uYDQwsJ4-OO3DErvuv4pBgVwk4?usp=sharing&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;</description>
      <category>LLM Engineering/Fine-Tuning</category>
      <category>finetuning</category>
      <category>Gemma</category>
      <category>GPU</category>
      <category>LLM</category>
      <category>unsloth</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/74</guid>
      <comments>https://striver.tistory.com/entry/Fine-Tuning-LLM-%ED%8C%8C%EC%9D%B8%ED%8A%9C%EB%8B%9D-%EC%86%94%EB%A3%A8%EC%85%98-Unsloth#entry74comment</comments>
      <pubDate>Wed, 26 Feb 2025 23:07:19 +0900</pubDate>
    </item>
    <item>
      <title>[Fine-Tuning] LLM fine-tuning (/w Elice Cloud) (2)</title>
      <link>https://striver.tistory.com/entry/DeepLearning-LLM-fine-tuning-w-Elice-Cloud-2</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;저번 글에 이어서 이번엔 파인 튜닝을 통해 추론 성능을 상승시켜보도록 하겠습니다. 어떻게 접근할지 고민하다가 누가 Dacon에 &lt;span style=&quot;color: #000000;&quot;&gt;&quot;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;b&gt;Gemma-2-2B-it Full Finetuning 모델&lt;/b&gt;&quot;을 공유 해주어서 이 코드를 먼저 실행 해 보았습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740204857039&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;!pip install transformers==4.40.1 accelerate==0.30.0 bitsandbytes==0.43.1 auto-gptq==0.7.1 autoawq==0.2.5 optimum==1.19.1 -qqq&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740234921084&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;!pip uninstall -y torch torchvision torchaudio
!pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu122&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740234931078&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;!pip uninstall -y bitsandbytes triton
!pip install --no-cache-dir bitsandbytes triton

!pip install -U transformers&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740234977258&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from datetime import datetime, timedelta, timezone

# KST (한국 표준시, UTC+9) 설정
kst = timezone(timedelta(hours=9))

# 현재 시간 (KST)
kst_now = datetime.now(kst)
print(kst_now.strftime(&quot;%Y-%m-%d %H:%M:%S KST&quot;))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740234983573&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import pandas as pd 
import torch 
from transformers import AutoTokenizer, AutoModelForCausalLM&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740234989993&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def remove_repeated_phrases(text):
    phrases = text.split(&quot; &quot;)
    seen = set()
    result = []
    for phrase in phrases:
        if phrase not in seen:
            result.append(phrase)
            seen.add(phrase)
    result[0] = result[0].replace(&quot;model\n&quot;, &quot;&quot;)
    return &quot; &quot;.join(result)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740235064771&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;train = pd.read_csv('./train.csv', encoding = 'utf-8-sig')
test = pd.read_csv('./test.csv', encoding = 'utf-8-sig')

samples = []

for i in range(10):
    sample = f&quot;input : {train['input'][i]} \n output : {train['output'][i]}&quot;
    samples.append(sample)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740235073866&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;model_name = &quot;mindw96/Gemma-2-2B-it-DACON-LLM&quot;
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype=torch.bfloat16,
    device_map=&quot;auto&quot;,
    trust_remote_code=True,
)

tokenizer = AutoTokenizer.from_pretrained(model_name)
tokenizer.pad_token = tokenizer.eos_token
tokenizer.padding_side = 'right'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740235083762&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;restored_reviews = []


for index, row in test.iterrows():
    query = row['input']
    system_prompt = f&quot;You are a helpful assistant specializing in restoring obfuscated Korean reviews. \
					Your task is to transform the given obfuscated Korean review into a clear, correct,\
					and natural-sounding Korean review that reflects its original meaning.\
					Below are examples of obfuscated Korean reviews and their restored forms:\n\n \
					Example, {samples[0]} \n {samples[1]} \n {samples[2]} \n {samples[3]} \n {samples[4]} \
					Spacing and word length in the output must be restored to the same as in the input.\
					Do not provide any description. Print only in Korean.&quot;

    messages = [
			{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: '{}\ninput: {}, output:'.format(system_prompt, query)}
		]
    input_ids = tokenizer.apply_chat_template(messages, return_tensors=&quot;pt&quot;, return_dict=True).to(&quot;cuda&quot;)

    outputs = model.generate(**input_ids, max_new_tokens=len(query))
    generated_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
    result = generated_text[len(messages[0]['content'])+6:].strip()
    result = remove_repeated_phrases(result)

    restored_reviews.append(result)
    print(index, result)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740235100044&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;submission = pd.read_csv('./sample_submission.csv', encoding = 'utf-8-sig')
submission['output'] = restored_reviews
submission.to_csv('./gemma_2b_it_submission.csv', index = False, encoding = 'utf-8-sig')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740235106000&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from datetime import datetime, timedelta, timezone

# KST (한국 표준시, UTC+9) 설정
kst = timezone(timedelta(hours=9))

# 현재 시간 (KST)
kst_now = datetime.now(kst)
print(kst_now.strftime(&quot;%Y-%m-%d %H:%M:%S KST&quot;))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드로 실행해보았는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1076&quot; data-origin-height=&quot;187&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Iy6Ej/btsMshaLxsZ/GzAy79WiLEwuCPATJ6V57k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Iy6Ej/btsMshaLxsZ/GzAy79WiLEwuCPATJ6V57k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Iy6Ej/btsMshaLxsZ/GzAy79WiLEwuCPATJ6V57k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIy6Ej%2FbtsMshaLxsZ%2FGzAy79WiLEwuCPATJ6V57k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1076&quot; height=&quot;187&quot; data-origin-width=&quot;1076&quot; data-origin-height=&quot;187&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1081&quot; data-origin-height=&quot;191&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BT0DB/btsMr9KtRJX/Zbdtxl6aLp7pskyMlFwhAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BT0DB/btsMr9KtRJX/Zbdtxl6aLp7pskyMlFwhAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BT0DB/btsMr9KtRJX/Zbdtxl6aLp7pskyMlFwhAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBT0DB%2FbtsMr9KtRJX%2FZbdtxl6aLp7pskyMlFwhAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1081&quot; height=&quot;191&quot; data-origin-width=&quot;1081&quot; data-origin-height=&quot;191&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10시간이 소요되었습니다... max_token을 Input 길이만큼 하도록 코드를 작성해서 배치 처리가 힘들었고, 제한된 gpu 메모리 상황에서 처리하다보니 생각보다 긴 시간이 소요되었습니다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1177&quot; data-origin-height=&quot;51&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/muPk3/btsMsWYatKl/KnIKaQ9JO8j2XidVJzpofK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/muPk3/btsMsWYatKl/KnIKaQ9JO8j2XidVJzpofK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/muPk3/btsMsWYatKl/KnIKaQ9JO8j2XidVJzpofK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmuPk3%2FbtsMsWYatKl%2FKnIKaQ9JO8j2XidVJzpofK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1177&quot; height=&quot;51&quot; data-origin-width=&quot;1177&quot; data-origin-height=&quot;51&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제출해보았는데 이전 샘플코드에 비해 확실히 정확도가 올라간 모습입니다. 그치만 위 소스코드는 남이 파인튜닝 해놓은 모델을 가져다 추론만 한 것이기때문에 제가 직접 모델을 파인튜닝해서 정확도를 상승시켜보고 싶단 생각이 들었습니다. 처음엔 2B 모델을 풀파인튜닝을 했을때 0.75의 수치가 나왔으니, 7~8B 정도의 모델로 풀 파인튜닝을 한다면 쉽게 성능이 나올 것이라고 생각했습니다. 하지만 문제는 너무나도 부족한 GPU 메모리였습니다. 엘리스 클라우드가 저렴하다고해도 고스펙 혹은 장시간 사용하기에는 금전적 부담이 됐습니다. &lt;u&gt;(제가 코드를 잘못 작성한건지 Epoch가 1일경우에도 몇십시간 걸려서 이건 아니다 싶었습니다...&lt;/u&gt;) 결국 모델의 크기를 낮추고 양자화를 해서 적은 리소스로 파인튜닝을 진행하였습니다. 대신 기존 train 데이터를 증강시켜서 더 다양한 패턴을 학습시키자는 생각이 들어서 데이터를 증강하는 방법을 선택했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740658794334&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import pandas as pd
from konoise import NoiseGenerator

# train.csv 파일 불러오기
train_df = pd.read_csv(&quot;train.csv&quot;)

# NoiseGenerator 객체 초기화 (konoise)
generator = NoiseGenerator()

# 증강된 데이터 저장할 리스트
augmented_data = []

# 각 데이터에 대해 증강
for idx, row in train_df.iterrows():
    input_text = row['input']
    output_text = row['output']
    
    # input 텍스트에 대해 노이즈 추가 (난독화된 텍스트 생성)
    augmented_input_list = generator.generate(input_text, methods='disattach-letters', prob=1.0)
    
    # 리스트에서 첫 번째 항목을 가져와서 슬라이싱
    augmented_input = augmented_input_list[0][0]
    
    # output 텍스트는 그대로 교정된 상태로 유지
    augmented_output = output_text.strip()  # 교정된 텍스트
    
    # 증강된 데이터를 리스트에 저장
    augmented_data.append([augmented_input, augmented_output])

# 증강된 데이터를 새로운 DataFrame에 저장
augmented_df = pd.DataFrame(augmented_data, columns=['input', 'output'])

# 증강된 데이터만 저장한 CSV 파일
augmented_df.to_csv(&quot;augmented_data.csv&quot;, index=False)

# 기존 데이터와 증강된 데이터를 합친 CSV 파일
combined_df = pd.concat([train_df[['input', 'output']], augmented_df], ignore_index=True)
combined_df.to_csv(&quot;combined_data.csv&quot;, index=False)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;konise 라이브러리를 이용해 기존 난독화 텍스트에 노이즈를 추가해 새로운 데이터셋을 만들었고, 기존 데이터셋과 결합한 csv 파일과 증강한 데이터셋만 가진 csv 파일 2개를 만들었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;897&quot; data-origin-height=&quot;346&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpYUAr/btsMvcMYMCe/uogvidSHBZn6KfCsjnK3ek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpYUAr/btsMvcMYMCe/uogvidSHBZn6KfCsjnK3ek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpYUAr/btsMvcMYMCe/uogvidSHBZn6KfCsjnK3ek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpYUAr%2FbtsMvcMYMCe%2FuogvidSHBZn6KfCsjnK3ek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;897&quot; height=&quot;346&quot; data-origin-width=&quot;897&quot; data-origin-height=&quot;346&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1001&quot; data-origin-height=&quot;294&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IYWnv/btsMuSB3vSM/dRnEBU6KKCR26ctoVOBNp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IYWnv/btsMuSB3vSM/dRnEBU6KKCR26ctoVOBNp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IYWnv/btsMuSB3vSM/dRnEBU6KKCR26ctoVOBNp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIYWnv%2FbtsMuSB3vSM%2FdRnEBU6KKCR26ctoVOBNp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1001&quot; height=&quot;294&quot; data-origin-width=&quot;1001&quot; data-origin-height=&quot;294&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터가 준비되었으니 파인튜닝을 진행하려했는데 파인튜닝에 필요한 여러 라이브러리들이 서로 의존성 충돌이 많이 일어나서 위와 같은 에러들이 너무 많이 발생하였습니다. 물론 버전만 잘 맞춰줬다면 실행에 큰 문제는 없었겠지만, 경험도 부족하고 시간도 부족하였기에 다른 사람들에게 조언을 구했습니다. 조언을 통해 제가 선택한 방법은 &lt;span style=&quot;color: #000000;&quot;&gt;&quot;&lt;u&gt;&lt;b&gt;unsloth&lt;/b&gt;&lt;/u&gt;&quot;&lt;span style=&quot;color: #333333;&quot;&gt;를 사용하는 것이었습니다. unsloth에 대한 자세한 소개는 다른 글에서 추가적으로 남기고 여기서는 &quot;단일 gpu 환경에서 적은 리소스로도 파인튜닝을 쉽게할 수 있도록 도와주는 편리한 라이브러리다. &quot;정도만 이해하고 넘어가시면 되겠습니다. 보일러플레이트 코드도 제공해주어서 자신의 task에 맞게 편하게 커스터마이징 할 수 있었습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;unsloth에서 지원하는 llm 모델중 선택해서 사용하면 되는구조였는데 저는 llama 3B 모델을 선택했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;723&quot; data-origin-height=&quot;159&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgnIqc/btsMwYCuNda/6puTMkzLxA44Az4lRUNC7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgnIqc/btsMwYCuNda/6puTMkzLxA44Az4lRUNC7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgnIqc/btsMwYCuNda/6puTMkzLxA44Az4lRUNC7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgnIqc%2FbtsMwYCuNda%2F6puTMkzLxA44Az4lRUNC7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;723&quot; height=&quot;159&quot; data-origin-width=&quot;723&quot; data-origin-height=&quot;159&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빠르게 프로토타입을 만들어보고자 Epoch를 1로 해서 학습시켜보았습니다. 몇십시간이 예상되던때와 다르게 2시간도 안돼서 학습이 완료됐습니다. 이어서 추론도 진행해보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;329&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bX0TKb/btsMx2jTPUT/WCdmFkkMRMoOAVejx6Hm3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bX0TKb/btsMx2jTPUT/WCdmFkkMRMoOAVejx6Hm3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bX0TKb/btsMx2jTPUT/WCdmFkkMRMoOAVejx6Hm3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbX0TKb%2FbtsMx2jTPUT%2FWCdmFkkMRMoOAVejx6Hm3k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1052&quot; height=&quot;329&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;329&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 간과한 사실이 있었습니다...&amp;nbsp; 초반 추론과정에선 문제가 없었는데 중간부터 input 데이터중에 모델의 최대 시퀀스 길이를 넘어가는 데이터가 있었던겁니다... 별 생각 없이 예제코드로나온 2048을 값으로 주면 충분할 것이라고 생각했는데 에러가 발생했고 input 데이터중 제일 길이가 큰 값이 몇인지 확인해보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740659631038&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import pandas as pd

# test.csv 파일을 불러옵니다.
test_df = pd.read_csv(&quot;test.csv&quot;)

max_len = 0
max_index = None

# 각 input 텍스트의 토큰 길이를 계산 (특수 토큰 포함 여부는 필요에 따라 조정)
for i, text in enumerate(test_df['input']):
    tokens = tokenizer.encode(text, add_special_tokens=True)
    if len(tokens) &amp;gt; max_len:
        max_len = len(tokens)
        max_index = i

print(&quot;최대 토큰 길이:&quot;, max_len)
print(&quot;해당 인덱스:&quot;, max_index)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대 토큰길이가 2439로 나왔습니다... 추론에 사용할 프롬프트도 생각해서 여유롭게 최대 시퀀스 값을 조정해야겠다는 생각이 들었습니다. 그래도 값을 4096으로 바꾸고 이참에 Epoch 수도 증가시켜서 테스트 해보기로했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;648&quot; data-origin-height=&quot;164&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/STtgN/btsMyIrEdZa/cksKJCmJqRHKNCWKrZ9MCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/STtgN/btsMyIrEdZa/cksKJCmJqRHKNCWKrZ9MCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/STtgN/btsMyIrEdZa/cksKJCmJqRHKNCWKrZ9MCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSTtgN%2FbtsMyIrEdZa%2FcksKJCmJqRHKNCWKrZ9MCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;648&quot; height=&quot;164&quot; data-origin-width=&quot;648&quot; data-origin-height=&quot;164&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3시간이라는 비교적 짧은 시간이 소요된 모습입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;789&quot; data-origin-height=&quot;327&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/twpW1/btsMx9iTaO1/KV1lzKeZJSVEU5vEN8Qsmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/twpW1/btsMx9iTaO1/KV1lzKeZJSVEU5vEN8Qsmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/twpW1/btsMx9iTaO1/KV1lzKeZJSVEU5vEN8Qsmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtwpW1%2FbtsMx9iTaO1%2FKV1lzKeZJSVEU5vEN8Qsmk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;789&quot; height=&quot;327&quot; data-origin-width=&quot;789&quot; data-origin-height=&quot;327&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용된 메모리도 많지 않습니다. 추론도 약 3시간 정도 소요되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1161&quot; data-origin-height=&quot;47&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgaCap/btsMzgICreJ/2ieZHEm9rpQKlzqJabCA5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgaCap/btsMzgICreJ/2ieZHEm9rpQKlzqJabCA5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgaCap/btsMzgICreJ/2ieZHEm9rpQKlzqJabCA5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgaCap%2FbtsMzgICreJ%2F2ieZHEm9rpQKlzqJabCA5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1161&quot; height=&quot;47&quot; data-origin-width=&quot;1161&quot; data-origin-height=&quot;47&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제출결과 Gemma 2B 풀파인튜닝 모델보다 더 좋은 성적을 받은 모습입니다! 대회를 늦게 접해 더 다양한 방법을 시도하며 최적화 해보지 못한게 아쉬웠지만 LLM Fine-Tuning을 경험해본 뜻깊은 시간이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dacon.io/competitions/official/236446/codeshare/12157?page=1&amp;amp;dtype=recent&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://dacon.io/competitions/official/236446/codeshare/12157?page=1&amp;amp;dtype=recent&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://huggingface.co/mindw96/Gemma-2-2B-it-DACON-LLM&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://huggingface.co/mindw96/Gemma-2-2B-it-DACON-LLM&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://issul.tistory.com/447&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://issul.tistory.com/447&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/wisenut-research/konoise&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/wisenut-research/konoise&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>LLM Engineering/Fine-Tuning</category>
      <category>elice-cloud</category>
      <category>fine-tuning</category>
      <category>LLM</category>
      <category>prompt</category>
      <category>unsloth</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/73</guid>
      <comments>https://striver.tistory.com/entry/DeepLearning-LLM-fine-tuning-w-Elice-Cloud-2#entry73comment</comments>
      <pubDate>Mon, 24 Feb 2025 23:09:17 +0900</pubDate>
    </item>
    <item>
      <title>[Fine-Tuning] LLM fine-tuning (/w Elice Cloud) (1)</title>
      <link>https://striver.tistory.com/entry/DeepLearning-LLM-fine-tuning-w-Elice-Cloud</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Dacon에서 진행하는 &quot;난독화된 한글 리뷰 복원 AI 경진대회&quot;를 뒤늦게 접하게 되어 LLM을 활용한 문제해결능력을 기르고자 경진대회에 참여하기로 하였습니다. 핵심 주제는 &quot;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;&lt;b&gt;식별하기 어렵게 쓴 한글 리뷰를 원래 한글 리뷰로 복원하는 AI 알고리즘 개발&lt;/b&gt;&quot; 이었습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;데이터셋과 샘플 코드를 모두 제공해주었기에 이를 먼저 실행해보았습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739951699982&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import pandas as pd
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig, pipeline

train = pd.read_csv('./drive/MyDrive/data_set/open/train.csv', encoding = 'utf-8-sig')
test = pd.read_csv('./drive/MyDrive/data_set/open/test.csv', encoding = 'utf-8-sig')

samples = []

for i in range(10):
    sample = f&quot;input : {train['input'][i]} \n output : {train['output'][i]}&quot;
    samples.append(sample)
    
    
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type= 'nf4',
    bnb_4bit_use_double_quant = True,
    bnb_4bit_compute_dtype=torch.bfloat16
)
model_id = 'beomi/gemma-ko-7b'
model = AutoModelForCausalLM.from_pretrained(model_id, quantization_config = bnb_config, device_map={&quot;&quot;:0})
tokenizer = AutoTokenizer.from_pretrained(model_id)
tokenizer.pad_token = tokenizer.eos_token
tokenizer.padding_side = 'right'

pipe = pipeline(
    task=&quot;text-generation&quot;,
    model=model,
    tokenizer=tokenizer
)

restored_reviews = []


for index, row in test.iterrows():
    query = row['input']

    messages = [
        {
            &quot;role&quot;: &quot;system&quot;,
            &quot;content&quot;: (
                &quot;You are a helpful assistant specializing in restoring obfuscated Korean reviews. &quot;
                &quot;Your task is to transform the given obfuscated Korean review into a clear, correct, &quot;
                &quot;and natural-sounding Korean review that reflects its original meaning. &quot;
                &quot;Below are examples of obfuscated Korean reviews and their restored forms:\n\n&quot;
                f&quot;Example, {samples}&quot;
                &quot;Spacing and word length in the output must be restored to the same as in the input. &quot;
                &quot;Do not provide any description. Print only in Korean.&quot;
            )
        },
        {
            &quot;role&quot;: &quot;user&quot;,
            &quot;content&quot;: f&quot;input : {query}, output : &quot;
        },
    ]

    prompt = &quot;\n&quot;.join([m[&quot;content&quot;] for m in messages]).strip()


    outputs = pipe(
        prompt,
        do_sample=True,
        temperature=0.2,
        top_p=0.9,
        max_new_tokens=len(query),
        eos_token_id=pipe.tokenizer.eos_token_id
    )

    generated_text = outputs[0]['generated_text']
    result = generated_text[len(prompt):].strip()


    restored_reviews.append(result)
    
submission = pd.read_csv('./drive/MyDrive/data_set/open/sample_submission.csv', encoding = 'utf-8-sig')
submission['output'] = restored_reviews
submission.to_csv('./baseline_submission.csv', index = False, encoding = 'utf-8-sig')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gemma 7b 모델을 4비트 양자화를 사용하여 추론하는 코드였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2435&quot; data-origin-height=&quot;371&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FHzOQ/btsMokq0XHk/6UF7yFqj7dCwvz7gJk6Flk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FHzOQ/btsMokq0XHk/6UF7yFqj7dCwvz7gJk6Flk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FHzOQ/btsMokq0XHk/6UF7yFqj7dCwvz7gJk6Flk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFHzOQ%2FbtsMokq0XHk%2F6UF7yFqj7dCwvz7gJk6Flk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2435&quot; height=&quot;371&quot; data-origin-width=&quot;2435&quot; data-origin-height=&quot;371&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글 코랩 무료 버전을 사용하니 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;거의 2시간을 추론과정에서 사용하더니&lt;/span&gt; OOM이 발생하였습니다...cpu 메모리도 사용하게 수정해서 다시 실행해도 똑같이 OOM이 발생하여 이번 기회에 클라우드 환경에서 코드를 실행하고 모델을 파인튜닝 해보도록 하였습니다. 비용이 너무 비싸서 고민하다가 &quot;elice cloud&quot;가 비용이 저렴하고 무료 크레딧을 제공하는걸 보고 사용해보게되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;809&quot; data-origin-height=&quot;637&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n2L2S/btsMnxDHxi9/wlYcBkEC44xe6Ium3qXLr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n2L2S/btsMnxDHxi9/wlYcBkEC44xe6Ium3qXLr0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n2L2S/btsMnxDHxi9/wlYcBkEC44xe6Ium3qXLr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn2L2S%2FbtsMnxDHxi9%2FwlYcBkEC44xe6Ium3qXLr0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;809&quot; height=&quot;637&quot; data-origin-width=&quot;809&quot; data-origin-height=&quot;637&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가입을하고 클라우드를 사용하려고할때&amp;nbsp;위와같이 기관 정보를 입력하라고 나오는데 크게 의미는 없고 그냥 도메인 정도라고 생각하시면 될 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2246&quot; data-origin-height=&quot;194&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OpjTP/btsMnPxiKlP/wwc6t9VkLMIdTbyUPEfMZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OpjTP/btsMnPxiKlP/wwc6t9VkLMIdTbyUPEfMZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OpjTP/btsMnPxiKlP/wwc6t9VkLMIdTbyUPEfMZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOpjTP%2FbtsMnPxiKlP%2Fwwc6t9VkLMIdTbyUPEfMZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2246&quot; height=&quot;194&quot; data-origin-width=&quot;2246&quot; data-origin-height=&quot;194&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트중인지 결제 수단을 등록하면 무료 크레딧을 제공해줘서 좋았습니다. 그럼 이제 본격적으로 인스턴스를 생성하고 GPU를 활용해 보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1544&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwS10A/btsMsTsNu5j/2ml74kDIXzHEm2ERdstvPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwS10A/btsMsTsNu5j/2ml74kDIXzHEm2ERdstvPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwS10A/btsMsTsNu5j/2ml74kDIXzHEm2ERdstvPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwS10A%2FbtsMsTsNu5j%2F2ml74kDIXzHEm2ERdstvPk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1544&quot; height=&quot;700&quot; data-origin-width=&quot;1544&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인스턴스가 여러 유형이 있는데 코랩 무료 버전보다는 gpu 메모리의 양이 많으면서도 최소한의 비용을 사용하고자 &quot;G-NAHPM-20&quot; 인스턴스를 선택하였습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1909&quot; data-origin-height=&quot;967&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yS8W4/btsMraJhg4G/GbrDP60ZXmGF7ZXxM3PwSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yS8W4/btsMraJhg4G/GbrDP60ZXmGF7ZXxM3PwSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yS8W4/btsMraJhg4G/GbrDP60ZXmGF7ZXxM3PwSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyS8W4%2FbtsMraJhg4G%2FGbrDP60ZXmGF7ZXxM3PwSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1909&quot; height=&quot;967&quot; data-origin-width=&quot;1909&quot; data-origin-height=&quot;967&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경은 주피터 노트북으로 하였고 크게 설정할 것 없이 유형과 환경을 지정하고 기다리면 금방 인스턴스가 생성되고 이용할 수 있어서 굉장히 편리했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;922&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MsGUe/btsMqPeiXre/32cHPRG0UFTJr8ityXBkpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MsGUe/btsMqPeiXre/32cHPRG0UFTJr8ityXBkpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MsGUe/btsMqPeiXre/32cHPRG0UFTJr8ityXBkpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMsGUe%2FbtsMqPeiXre%2F32cHPRG0UFTJr8ityXBkpk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1915&quot; height=&quot;922&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;922&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상단 크레딧이 시간 단위가 아니라 실시간으로 차감 돼서 지출이 직관적으로 보여서 좋았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1054&quot; data-origin-height=&quot;256&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZWABB/btsMr0e7B3p/yvdOVghF8ADCkcqnGTUz7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZWABB/btsMr0e7B3p/yvdOVghF8ADCkcqnGTUz7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZWABB/btsMr0e7B3p/yvdOVghF8ADCkcqnGTUz7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZWABB%2FbtsMr0e7B3p%2FyvdOVghF8ADCkcqnGTUz7K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1054&quot; height=&quot;256&quot; data-origin-width=&quot;1054&quot; data-origin-height=&quot;256&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인스턴스가 잘 생성되었는지 Gpu 메모리를 확인해보았고, 잘 만들어진 모습을 볼 수 있었습니다. 이제 기존 코랩에서 OOM이 발생했던 샘플코드를 다시 실행해 보았습니다. 그런데 샘플 코드를 실행하기에 GPU 메모리가 부족해서 다른 에러가 발생했어. 40GB의 인스턴스를 다시 생성해서 진행하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;456&quot; data-origin-height=&quot;190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8hCTH/btsMsTtrDnn/rvNDgzzVip7XPi7OPNAlgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8hCTH/btsMsTtrDnn/rvNDgzzVip7XPi7OPNAlgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8hCTH/btsMsTtrDnn/rvNDgzzVip7XPi7OPNAlgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8hCTH%2FbtsMsTtrDnn%2FrvNDgzzVip7XPi7OPNAlgk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;456&quot; height=&quot;190&quot; data-origin-width=&quot;456&quot; data-origin-height=&quot;190&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 시작 시간이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;503&quot; data-origin-height=&quot;198&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFcwOD/btsMuhNBkHX/ElJzANkeubMTouWAZFf261/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFcwOD/btsMuhNBkHX/ElJzANkeubMTouWAZFf261/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFcwOD/btsMuhNBkHX/ElJzANkeubMTouWAZFf261/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFcwOD%2FbtsMuhNBkHX%2FElJzANkeubMTouWAZFf261%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;503&quot; height=&quot;198&quot; data-origin-width=&quot;503&quot; data-origin-height=&quot;198&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추론이 모두 끝났을때 시간입니다. 약 5시간 정도가 소모됐고,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;762&quot; data-origin-height=&quot;498&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chLene/btsMt7Yyox4/c4A16mhz9kc7trhtLnIs10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chLene/btsMt7Yyox4/c4A16mhz9kc7trhtLnIs10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chLene/btsMt7Yyox4/c4A16mhz9kc7trhtLnIs10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchLene%2FbtsMt7Yyox4%2Fc4A16mhz9kc7trhtLnIs10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;762&quot; height=&quot;498&quot; data-origin-width=&quot;762&quot; data-origin-height=&quot;498&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPU 메모리를 30GB정도 사용한 모습입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1097&quot; data-origin-height=&quot;48&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tdKNS/btsMtPKDeXG/bxGPt4YrsIaGHnHmJnoytK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tdKNS/btsMtPKDeXG/bxGPt4YrsIaGHnHmJnoytK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tdKNS/btsMtPKDeXG/bxGPt4YrsIaGHnHmJnoytK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtdKNS%2FbtsMtPKDeXG%2FbxGPt4YrsIaGHnHmJnoytK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1097&quot; height=&quot;48&quot; data-origin-width=&quot;1097&quot; data-origin-height=&quot;48&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;test 데이터셋 추론 결과를 제출하니 샘플 코드답게 현저히 낮은 스코어를 볼 수 있었습니다. 이제 여러 기법들을 활용해 추론 시간을 단축시키고 추론 성능을 상승시켜보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://elice.io/ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://elice.io/ko&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dacon.io/competitions/official/236446/leaderboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://dacon.io/competitions/official/236446/leaderboard&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>LLM Engineering/Fine-Tuning</category>
      <category>dacon</category>
      <category>elice</category>
      <category>GPU</category>
      <category>엘리스 클라우드</category>
      <category>인스턴스</category>
      <category>클라우드</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/72</guid>
      <comments>https://striver.tistory.com/entry/DeepLearning-LLM-fine-tuning-w-Elice-Cloud#entry72comment</comments>
      <pubDate>Wed, 19 Feb 2025 23:12:45 +0900</pubDate>
    </item>
    <item>
      <title>[DeepLearning] 언어 모델 최적화 개념 정리</title>
      <link>https://striver.tistory.com/entry/DeepLearning</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;언어 모델을 효과적으로 활용하려면 모델을 최적화하고 경량화하는 과정이 중요합니다. 이번 포스트에서는 &lt;/span&gt;&lt;span&gt;&lt;b&gt;모델의 추론 과정, KV 캐시, 양자화, 지식 증류&lt;/b&gt;&lt;/span&gt;&lt;span&gt; 등의 개념을 직관적으로 정리해보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;1. 언어 모델이 텍스트를 생성하는 방식&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-end=&quot;346&quot; data-start=&quot;317&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1.1 언어 모델이 텍스트 생성을 마치는 이유&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;483&quot; data-start=&quot;347&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;425&quot; data-start=&quot;347&quot;&gt;&lt;b&gt;EOS(End of Sequence) 토큰 생성&lt;/b&gt;&lt;br /&gt;문장이 끝났음을 알리는 특수 토큰을 생성할 경우 텍스트 생성을 종료합니다.&lt;/li&gt;
&lt;li data-end=&quot;483&quot; data-start=&quot;426&quot;&gt;&lt;b&gt;최대 길이 도달&lt;/b&gt;&lt;br /&gt;사용자가 설정한 최대 토큰 길이에 도달하면 텍스트 생성을 종료합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;519&quot; data-start=&quot;485&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1.2 자기 회귀적(Auto-Regressive) 모델&lt;/b&gt;&lt;/h4&gt;
&lt;p data-end=&quot;617&quot; data-start=&quot;520&quot; data-ke-size=&quot;size16&quot;&gt;언어 모델은 입력된 텍스트를 기반으로 다음 토큰을 하나씩 순차적으로 예측합니다. 즉, 이전에 생성된 토큰들을 다시 모델 입력으로 넣어 다음 토큰을 예측하는 과정을 반복합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;2. 중복 연산을 줄이는 KV 캐시&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-end=&quot;674&quot; data-start=&quot;648&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.1 KV(Key-Value) 캐시란?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-end=&quot;798&quot; data-start=&quot;675&quot; data-ke-size=&quot;size16&quot;&gt;셀프 어텐션(self-attention) 과정에서 이미 계산된 키(Key)와 값(Value)을 저장해두었다가 재활용함으로써 중복 연산을 줄이는 기법입니다. 이를 통해 모델 추론 시 연산 비용과 시간을 절감할 수 있습니다.&lt;/p&gt;
&lt;h4 data-end=&quot;825&quot; data-start=&quot;800&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.2 KV 캐시의 메모리 사용량 예시&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1084&quot; data-start=&quot;826&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;903&quot; data-start=&quot;826&quot;&gt;&lt;b&gt;계산식 예시&lt;/b&gt;:&lt;br /&gt;2(fp16) &amp;times; 2(키와 값) &amp;times; 레이어 수 &amp;times; 토큰 임베딩 차원 &amp;times; 최대 시퀀스 길이 &amp;times; 배치 크기&lt;/li&gt;
&lt;li data-end=&quot;1084&quot; data-start=&quot;904&quot;&gt;&lt;b&gt;예: Llama-2 13B 모델 기준&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1084&quot; data-start=&quot;935&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;964&quot; data-start=&quot;935&quot;&gt;배치 크기 1당 약 3.125GB 메모리 사용&lt;/li&gt;
&lt;li data-end=&quot;1025&quot; data-start=&quot;967&quot;&gt;NVIDIA A100(40GB)에서 최대 14GB까지 KV 캐시 활용 가능 &amp;rarr; 배치 크기 최대 4&lt;/li&gt;
&lt;li data-end=&quot;1084&quot; data-start=&quot;1028&quot;&gt;더 큰 배치 크기를 처리하려면 모델 또는 KV 캐시를 효율적으로 줄이는 추가 최적화가 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;3. GPU 구조와 최적의 배치 크기&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-end=&quot;1141&quot; data-start=&quot;1116&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3.1 효율적인 서빙을 위한 주요 지표&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;1267&quot; data-start=&quot;1142&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;1174&quot; data-start=&quot;1142&quot;&gt;&lt;b&gt;비용&lt;/b&gt;: GPU 등 하드웨어 자원 사용 효율&lt;/li&gt;
&lt;li data-end=&quot;1222&quot; data-start=&quot;1175&quot;&gt;&lt;b&gt;처리량(Throughput)&lt;/b&gt;: 일정 시간(초)당 처리 가능한 요청 수&lt;/li&gt;
&lt;li data-end=&quot;1267&quot; data-start=&quot;1223&quot;&gt;&lt;b&gt;지연 시간(Latency)&lt;/b&gt;: 하나의 토큰을 생성하는 데 걸리는 시간&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-end=&quot;1286&quot; data-start=&quot;1269&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3.2 GPU 내부 구조&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;313&quot; data-origin-height=&quot;338&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjA2Qu/btsMjFPJ5Be/RFwmF7CuAjR9f7QHOhsrS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjA2Qu/btsMjFPJ5Be/RFwmF7CuAjR9f7QHOhsrS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjA2Qu/btsMjFPJ5Be/RFwmF7CuAjR9f7QHOhsrS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjA2Qu%2FbtsMjFPJ5Be%2FRFwmF7CuAjR9f7QHOhsrS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;313&quot; height=&quot;338&quot; data-origin-width=&quot;313&quot; data-origin-height=&quot;338&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1463&quot; data-start=&quot;1287&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1347&quot; data-start=&quot;1287&quot;&gt;&lt;b&gt;SM(Stream Multiprocessors)&lt;/b&gt;&lt;br /&gt;Compute(연산)을&amp;nbsp;수행하는&amp;nbsp;부분과,&amp;nbsp;계산&amp;nbsp;할&amp;nbsp;값을&amp;nbsp;저장하는&amp;nbsp;SRAM으로&amp;nbsp;구성&lt;/li&gt;
&lt;li data-end=&quot;1403&quot; data-start=&quot;1348&quot;&gt;&lt;b&gt;SRAM(Static RAM)&lt;/b&gt;&lt;br /&gt;L1 캐시 또는 공유 메모리 역할, 용량이 적음&lt;/li&gt;
&lt;li data-end=&quot;1463&quot; data-start=&quot;1404&quot;&gt;&lt;b&gt;HBM(High Bandwidth Memory)&lt;/b&gt;&lt;br /&gt;대규모 데이터를 저장하는 고대역폭 메모리&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;1486&quot; data-start=&quot;1465&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3.3 최적의 배치 크기(B*)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1761&quot; data-start=&quot;1487&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1548&quot; data-start=&quot;1487&quot;&gt;배치 크기가 작으면 모델 파라미터 이동(메모리 I/O)에 시간이 많이 들고, 연산 자원 낭비가 발생합니다.&lt;/li&gt;
&lt;li data-end=&quot;1591&quot; data-start=&quot;1549&quot;&gt;배치 크기가 너무 크면 연산 시간이 길어져 전체 지연 시간이 늘어납니다.&lt;/li&gt;
&lt;li data-end=&quot;1761&quot; data-start=&quot;1592&quot;&gt;최적 배치 크기 공식 예시:&lt;br /&gt;B* = 하드웨어 연산 속도 / (2 &amp;times; 메모리 대역폭)
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1761&quot; data-start=&quot;1652&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1690&quot; data-start=&quot;1652&quot;&gt;A100 GPU 기준 약 102 정도가 최적 배치 크기로 추정&lt;/li&gt;
&lt;li data-end=&quot;1761&quot; data-start=&quot;1693&quot;&gt;그러나 Llama-2 13B 모델은 실제 배치 크기가 4 정도로 제한 &amp;rarr; 추가적인 모델 및 메모리 최적화가 요구됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;4. KV 캐시 메모리 줄이기&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4.1 효율적인 어텐션 방식&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;2021&quot; data-start=&quot;1809&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;1894&quot; data-start=&quot;1809&quot;&gt;&lt;b&gt;멀티 쿼리 어텐션(Multi-Query Attention)&lt;/b&gt;&lt;br /&gt;모든 쿼리 토큰이 하나의 키와 값을 공유해 메모리를 절약하는 방식입니다.&lt;/li&gt;
&lt;li data-end=&quot;2021&quot; data-start=&quot;1895&quot;&gt;&lt;b&gt;그룹 쿼리 어텐션(Group Query Attention)&lt;/b&gt;&lt;br /&gt;멀티 헤드 어텐션과 멀티 쿼리 어텐션의 절충안으로, 여러 쿼리를 그룹으로 묶어 키/값을 공유함으로써 메모리 사용량과 성능 간의 균형을 유지합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;5. 양자화(Quantization)로 모델 크기 줄이기&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;양자화란 모델의 가중치와 연산을 정수 기반의 낮은 정밀도로 표현하여 메모리를 절감하고 추론 속도를 높이는 방법입니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-end=&quot;2160&quot; data-start=&quot;2131&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5.1 비츠앤바이츠 (BitsAndBytes)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2300&quot; data-start=&quot;2161&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2232&quot; data-start=&quot;2161&quot;&gt;&lt;b&gt;8비트 행렬 연산&lt;/b&gt;&lt;br /&gt;이상치가 포함된 열은 16비트로 유지하고, 정상 범위 값들은 8비트로 양자화하여 연산합니다.&lt;/li&gt;
&lt;li data-end=&quot;2300&quot; data-start=&quot;2233&quot;&gt;&lt;b&gt;4비트 정규 분포 양자화(QLoRA)&lt;/b&gt;&lt;br /&gt;보다 높은 수준의 메모리 절감 효과를 기대할 수 있는 방식입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;2333&quot; data-start=&quot;2302&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5.2 GPTQ (GPT Quantization)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2418&quot; data-start=&quot;2334&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2378&quot; data-start=&quot;2334&quot;&gt;양자화 전후 모델 예측값의 차이를 최소화하도록 모델 파라미터를 조정하는 기법&lt;/li&gt;
&lt;li data-end=&quot;2418&quot; data-start=&quot;2379&quot;&gt;예: 175B 규모 모델 양자화에 A100 GPU로 약 4시간 소요&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;2470&quot; data-start=&quot;2420&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5.3 AWQ (Activation-aware Weight Quantization)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2614&quot; data-start=&quot;2471&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2559&quot; data-start=&quot;2471&quot;&gt;모델의 모든 파라미터가 동일하게 중요하지 않다는 점을 고려하여, 활성화 값(Activation Magnitude)이 큰 채널의 파라미터를 더 중요하게 반영&lt;/li&gt;
&lt;li data-end=&quot;2614&quot; data-start=&quot;2560&quot;&gt;스케일러(Scaler)를 적용해 양자화 시 발생할 수 있는 정밀도 손실을 보완할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;6. 지식 증류(Knowledge Distillation)&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;div&gt;지식 증류는 성능이 높은 대형 모델(Teacher Model)의 출력을 작은 모델(Student Model)에 학습시켜 성능을 향상하는 방법입니다. 작은 모델도 선생 모델이 가진 지식을 효율적으로 흡수할 수 있어, 파라미터 수는 적어도 성능을 높게 유지할 수 있습니다.
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-end=&quot;2821&quot; data-start=&quot;2815&quot; data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-end=&quot;2980&quot; data-start=&quot;2823&quot; data-ke-size=&quot;size16&quot;&gt;이상으로 언어 모델을 경량화하고 최적화하는 주요 방법들을 간략히 살펴보았습니다. 실제 환경에서 &lt;b&gt;KV 캐시&lt;/b&gt;를 활용하고, &lt;b&gt;배치 크기를 최적화&lt;/b&gt;하며, &lt;b&gt;양자화&lt;/b&gt;와 &lt;b&gt;지식 증류&lt;/b&gt; 같은 기술을 적절히 조합하면 더 적은 자원으로도 빠르고 효율적인 모델 서빙이 가능해집니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3058&quot; data-start=&quot;2982&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2999&quot; data-start=&quot;2982&quot;&gt;&lt;b&gt;KV 캐시 최적화&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;3016&quot; data-start=&quot;3000&quot;&gt;&lt;b&gt;배치 크기 조절&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;3038&quot; data-start=&quot;3017&quot;&gt;&lt;b&gt;양자화를 통한 모델 축소&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;3058&quot; data-start=&quot;3039&quot;&gt;&lt;b&gt;지식 증류 기반 압축&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;LLM을 활용한 실전 AI 애플리케이션 개발 - 허정준&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=gMOAud7hZg4&amp;amp;t=1980s&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=gMOAud7hZg4&amp;amp;t=1980s&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/DeepLearning</category>
      <category>awq</category>
      <category>gptq</category>
      <category>GPU</category>
      <category>kv 캐시</category>
      <category>그룹 쿼리 어텐션</category>
      <category>양자화</category>
      <category>지식 증류</category>
      <category>최적화</category>
      <category>추론</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/70</guid>
      <comments>https://striver.tistory.com/entry/DeepLearning#entry70comment</comments>
      <pubDate>Fri, 14 Feb 2025 21:06:32 +0900</pubDate>
    </item>
    <item>
      <title>[DeepLearning] 용어 정리</title>
      <link>https://striver.tistory.com/entry/DeepLearning-%EC%9A%A9%EC%96%B4-%EB%B0%8F-%EC%96%B4%ED%9C%98</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;공부하면서 생소한 용어나 원활한 플로우 이해를 돕기 위한 포스팅 공간을 마련하였습니다. 계속 수정해가면서 내용이 추가될 예정입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;파라미터(P&lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;arameter&lt;/span&gt;)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델의 파라미터는 머신러닝 모델이 학습을 통해 조정하는 값들로, 모델의 예측 성능을 결정하는 중요한 요소입니다. 파라미터는 모델의 구조에 따라 다르며, 주로 가중치(weights)와 편향(biases)으로 구성됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가중치(Weights)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력&amp;nbsp;데이터의&amp;nbsp;각&amp;nbsp;특징(feature)에&amp;nbsp;곱해지는&amp;nbsp;값입니다.&amp;nbsp;가중치는&amp;nbsp;모델이&amp;nbsp;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;입력&amp;nbsp;데이터의&amp;nbsp;중요도를&amp;nbsp;학습&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;하는&amp;nbsp;데&amp;nbsp;사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;편향(Biases)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델의&amp;nbsp;출력에&amp;nbsp;더해지는&amp;nbsp;상수&amp;nbsp;값입니다.&amp;nbsp;편향은&amp;nbsp;모델이&amp;nbsp;데이터를&amp;nbsp;더&amp;nbsp;잘&amp;nbsp;맞추기&amp;nbsp;위해&amp;nbsp;조정됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시: 선형 회귀 모델&lt;/p&gt;
&lt;pre id=&quot;code_1739111757023&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[ y = w_1 x_1 + w_2 x_2 + b ]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(&amp;nbsp;w_1&amp;nbsp;)과&amp;nbsp;(&amp;nbsp;w_2&amp;nbsp;)는&amp;nbsp;가중치&amp;nbsp;파라미터입니다.&lt;/li&gt;
&lt;li&gt;( b )는 편향 파라미터입니다.&lt;/li&gt;
&lt;li&gt;( x_1 )과 ( x_2 )는 입력 데이터의 특징입니다.&lt;/li&gt;
&lt;li&gt;(&amp;nbsp;y&amp;nbsp;)는&amp;nbsp;모델의&amp;nbsp;출력입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;그래디언트(Gradient)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래디언트는&amp;nbsp;함수의&amp;nbsp;기울기를&amp;nbsp;나타내는&amp;nbsp;벡터입니다.&amp;nbsp;머신러닝에서&amp;nbsp;그래디언트는&amp;nbsp;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;손실&amp;nbsp;함수의&amp;nbsp;출력이&amp;nbsp;모델의&amp;nbsp;각&amp;nbsp;파라미터에&amp;nbsp;대해&amp;nbsp;얼마나&amp;nbsp;변화하는지&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;를&amp;nbsp;나타냅니다.&amp;nbsp;즉,&amp;nbsp;그래디언트는&amp;nbsp;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;손실&amp;nbsp;함수의&amp;nbsp;변화율&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;을&amp;nbsp;나타내며,&amp;nbsp;이를&amp;nbsp;통해&amp;nbsp;모델의&amp;nbsp;파라미터를&amp;nbsp;업데이트하여&amp;nbsp;손실을&amp;nbsp;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;최소화&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;학습률(Learning Rate)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래디언트의 크기를 조절하는 중요한 하이퍼파라미터입니다. 학습률은 그래디언트를 얼마나 크게 적용할지를 결정합니다. 높은 학습률은 빠른 학습을 가능하게 하지만, 불안정할 수 있습니다. 반대로, 낮은 학습률은 안정적인 학습을 가능하게 하지만, 느릴 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;손실(Loss)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델의 예측값과 실제 레이블간의 차이를 의미합니다. 손실 값이 작을수록 모델의 예측이 실제 값에 가까워진다는 의미입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;손실 함수(Loss Function)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델의 예측값과 실제 레이블 간의 차이를 수치화하는 함수입니다. 손실 함수는 모델이 얼마나 잘 예측하고 있는지를 평가하는 데 사용됩니다. 손실 함수의 출력은 손실 값입니다. 손실 함수의 예시는 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;평균 제곱 오차(MSE, Mean Squared Error)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;회귀 문제에서 자주 사용됩니다. 예측값과 실제 값의 차이를 제곱한 후 평균을 구합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;교차 엔트로피 손실(Cross-Entropy Loss)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;분류 문제에서 자주 사용됩니다. 예측 확률 분포와 실제 레이블 간의 차이를 계산합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;옵티마이저(Optimizer)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;step() 메서드를 활용해서 역전파 결과를 바탕으로 모델의 파라미터를 업데이트합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;역전파(Backpropagation)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역전파는 신경망의 학습 과정에서 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;그래디언트를 계산하는 알고리즘&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;입니다. 역전파는 다음과 같은 단계로 이루어집니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;순전파(Forward Pass): 입력 데이터를 모델에 통과시켜 예측값을 계산합니다.&lt;/li&gt;
&lt;li&gt;손실 계산(Loss Calculation): 예측값과 실제 레이블 간의 차이를 통해 손실을 계산합니다.&lt;/li&gt;
&lt;li&gt;역전파(Backward Pass): 손실을 통해 그래디언트를 계산합니다. 이 과정에서 체인 룰(chain rule)을 사용하여 각 파라미터에 대한 그래디언트를 계산합니다.&lt;/li&gt;
&lt;li&gt;파라미터&amp;nbsp;업데이트(Parameter&amp;nbsp;Update):&amp;nbsp;옵티마이저를&amp;nbsp;사용하여&amp;nbsp;그래디언트를&amp;nbsp;기반으로&amp;nbsp;모델의&amp;nbsp;파라미터를&amp;nbsp;업데이트합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;역전파 기반 모델 업데이트 원리&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역전파를 통해 계산된 그래디언트를 사용하여 모델의 파라미터를 업데이트하는 과정은 다음과 같습니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;순전파(Forward&amp;nbsp;Pass):&amp;nbsp;입력&amp;nbsp;데이터를&amp;nbsp;모델에&amp;nbsp;통과시켜&amp;nbsp;예측값을&amp;nbsp;계산합니다.&lt;/li&gt;
&lt;li&gt;손실&amp;nbsp;계산(Loss&amp;nbsp;Calculation):&amp;nbsp;예측값과&amp;nbsp;실제&amp;nbsp;레이블&amp;nbsp;간의&amp;nbsp;차이를&amp;nbsp;통해&amp;nbsp;손실을&amp;nbsp;계산합니다.&lt;/li&gt;
&lt;li&gt;역전파(Backward&amp;nbsp;Pass):&amp;nbsp;손실을&amp;nbsp;통해&amp;nbsp;그래디언트를&amp;nbsp;계산합니다.&lt;/li&gt;
&lt;li&gt;그래디언트&amp;nbsp;초기화(Gradient&amp;nbsp;Zeroing):&amp;nbsp;옵티마이저의&amp;nbsp;zero_grad()&amp;nbsp;메서드를&amp;nbsp;호출하여&amp;nbsp;이전&amp;nbsp;배치에서&amp;nbsp;계산된&amp;nbsp;그래디언트를&amp;nbsp;초기화합니다.&lt;/li&gt;
&lt;li&gt;그래디언트&amp;nbsp;계산(Gradient&amp;nbsp;Calculation):&amp;nbsp;손실의&amp;nbsp;그래디언트를&amp;nbsp;계산하여&amp;nbsp;각&amp;nbsp;파라미터에&amp;nbsp;대한&amp;nbsp;변화율을&amp;nbsp;구합니다.&lt;/li&gt;
&lt;li&gt;파라미터&amp;nbsp;업데이트(Parameter&amp;nbsp;Update):&amp;nbsp;옵티마이저의&amp;nbsp;step()&amp;nbsp;메서드를&amp;nbsp;호출하여&amp;nbsp;그래디언트를&amp;nbsp;기반으로&amp;nbsp;모델의&amp;nbsp;파라미터를&amp;nbsp;업데이트합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/DeepLearning</category>
      <category>가중치</category>
      <category>그래디언트</category>
      <category>선형 회귀</category>
      <category>손실</category>
      <category>손실함수</category>
      <category>역전파</category>
      <category>옵타마이저</category>
      <category>파라미터</category>
      <category>편향</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/69</guid>
      <comments>https://striver.tistory.com/entry/DeepLearning-%EC%9A%A9%EC%96%B4-%EB%B0%8F-%EC%96%B4%ED%9C%98#entry69comment</comments>
      <pubDate>Wed, 12 Feb 2025 23:31:39 +0900</pubDate>
    </item>
    <item>
      <title>[DeepLearning] 제목 기반 카테고리 예측 모델 개발</title>
      <link>https://striver.tistory.com/entry/DeepLearning-%EC%B9%B4%ED%85%8C%EA%B3%A0%EB%A6%AC-%EB%B6%84%EB%A5%98-%EB%AA%A8%EB%8D%B8</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;본 내용은 하단 참고자료에 작성된 책에 대한 내용을 기반으로 다시한번 정리한 내용입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 통해 &lt;span&gt;연합뉴스 기사의 제목을 바탕으로 카테고리를 예측하는 딥러닝 모델을 개발하는 과정을 정리하였습니다. 이를 위해 데이터셋 로드부터 모델 학습 및 평가까지의 전체 과정을 단계별로 설명하며, 주요 개념과 코드 실행 결과를 함께 살펴보도록 하겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 모델 학습에 사용할 연합뉴스 데이터셋 다운로드&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;모델 학습을 위해 KLUE 데이터셋의 YNAT 서브셋을 사용합니다. &lt;/span&gt;&lt;span&gt;datasets&lt;/span&gt;&lt;span&gt; 라이브러리의 &lt;/span&gt;&lt;span&gt;load_dataset&lt;/span&gt;&lt;span&gt; 함수를 이용하여 데이터를 로드합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739013251713&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from datasets import load_dataset
klue_tc_train = load_dataset('klue', 'ynat', split='train')
klue_tc_eval = load_dataset('klue', 'ynat', split='validation')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;데이터를 출력해 보면 다음과 같은 구조를 확인할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739019410387&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# klue_tc_train
Dataset({
    features: ['guid', 'title', 'label', 'url', 'date'],
    num_rows: 45678
})

# klue_tc_train[0]
{'guid': 'ynat-v1_train_00000',
 'title': '유튜브 내달 2일까지 크리에이터 지원 공간 운영',
 'label': 3,
 'url': 'https://news.naver.com/main/read.nhn?mode=LS2D&amp;amp;mid=shm&amp;amp;sid1=105&amp;amp;sid2=227&amp;amp;oid=001&amp;amp;aid=0008508947',
 'date': '2016.06.30. 오전 10:36'}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;각 샘플은 기사 제목(&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;), 카테고리(&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;), 기사 링크(&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;), 날짜(&lt;/span&gt;&lt;span&gt;date&lt;/span&gt;&lt;span&gt;) 등의 정보를 포함하고 있습니다. &lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt; 값은 숫자로 되어 있어 어떤 카테고리에 해당하는지 직관적으로 이해하기 어려우므로, 카테고리명을 확인해 보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739019707428&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# klue_tc_train.features['label'].names
['IT과학', '경제', '사회', '생활문화', '세계', '스포츠', '정치']&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 불필요한 컬럼 제거&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;모델 학습에 불필요한 컬럼(&lt;/span&gt;&lt;span&gt;guid&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;date&lt;/span&gt;&lt;span&gt;)을 제거하고, 필요한 컬럼(&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;)만 남기도록 하겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739019532631&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;klue_tc_train = klue_tc_train.remove_columns(['guid', 'url', 'date'])
klue_tc_eval = klue_tc_eval.remove_columns(['guid', 'url', 'date'])

# klue_tc_train
Dataset({
     features: ['title', 'label'],
     num_rows: 45678
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 카테고리를&amp;nbsp;문자로&amp;nbsp;표기한&amp;nbsp;label_str&amp;nbsp;컬럼&amp;nbsp;추가&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 컬럼들을 남겨놓았으나, label이 아직 숫자라 가독성이 떨어진다는 단점이 있습니다. 카테고리를 확인하기 쉽도록 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;새롭게 label_str 컬럼을 추가해주겠습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739019838132&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# klue_tc_train.features['label']
ClassLabel(names=['IT과학', '경제', '사회', '생활문화', '세계', '스포츠', '정치'], id=None)

klue_tc_label = klue_tc_train.features['label']

def make_str_label(batch):
  batch['label_str'] = klue_tc_label.int2str(batch['label'])
  return batch

klue_tc_train = klue_tc_train.map(make_str_label, batched=True, batch_size=1000)

# klue_tc_train[0]
{'title': '유튜브 내달 2일까지 크리에이터 지원 공간 운영', 'label': 3, 'label_str': '생활문화'}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;klue_tc_train.features['label'] 을 출력해보면 ClassLabel 객체로 이루어진걸 볼 수 있습니다. 해당 객체는 숫자를 입력하면 해당 숫자에 맵핑된 카테고리를 반환해주는 int2str() 함수를 지니고 있습니다. 그리고 데이터셋의 요소별로 함수를 실행시켜주는 map 함수를 이용해서 batch_size씩 make_str_label을 실행해서 데이터 요소별로 label_str 컬럼을 만들어주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;4. 학습/검증/테스트&amp;nbsp;데이터셋&amp;nbsp;분할&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빠른 실습 진행을 위해 &lt;u&gt;&lt;b&gt;학습 데이터셋&lt;/b&gt;&lt;/u&gt;을 부분적으로 추출하고 학습이 잘 되고 있는지 확인할 검증 데이터와 성능 확인에 사용할 테스트 데이터는 &lt;u&gt;&lt;b&gt;검증 데이터셋&lt;/b&gt;&lt;/u&gt;에서 각각 추출하여 사용하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739020240405&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;train_dataset = klue_tc_train.train_test_split(test_size=10000, shuffle=True, seed=42)['test']
dataset = klue_tc_eval.train_test_split(test_size=1000, shuffle=True, seed=42)
test_dataset = dataset['test']
valid_dataset = dataset['train'].train_test_split(test_size=1000, shuffle=True, seed=42)['test']&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;train_test_split 메서드를 사용하면 입력한 test_size or train_size 값을 기준으로 학습 데이터셋과 테스트 데이터셋을 분리해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;shuffle는 데이터를 섞어서 분할한다는 의미를 지니고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739020312703&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;train_dataset = klue_tc_train.train_test_split(test_size=10000, shuffle=True, seed=42)

# train_dataset
DatasetDict({
    train: Dataset({
        features: ['guid', 'title', 'label', 'url', 'date'],
        num_rows: 35678
    })
    test: Dataset({
        features: ['guid', 'title', 'label', 'url', 'date'],
        num_rows: 10000
    })
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 확인했던 전체 데이터수 45678개에서 명시한 test_size만큼 test 데이터셋이 생기고 나머지는 train 데이터셋으로 생성된 모습을 볼 수 있습니다. 테스트 데이터와 검증 데이터는 서로 다른 데이터를 사용해야하기 때문에 klue_tc_eval 데이터셋에서 1차적으로 train_test_split 메서드를 사용한 dataset의 test 데이터셋을 테스트 데이터로 이용했고, 남은 train 데이터셋에서 한번 더 train_test_split 메서드를 활용해 검증 데이터를 생성한 모습을 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;5. Trainer를 사용한 학습&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 모든 데이터셋을 준비하였으니 모델을 학습 시키도록 하겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1739020688523&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import torch
import numpy as np
from transformers import (
    Trainer,
    TrainingArguments,
    AutoModelForSequenceClassification,
    AutoTokenizer
)

def tokenize_function(examples):
    return tokenizer(examples[&quot;title&quot;], padding=&quot;max_length&quot;, truncation=True)

model_id = &quot;klue/roberta-base&quot;
model = AutoModelForSequenceClassification.from_pretrained(model_id, num_labels=len(train_dataset.features['label'].names))
tokenizer = AutoTokenizer.from_pretrained(model_id)

train_dataset = train_dataset.map(tokenize_function, batched=True)
valid_dataset = valid_dataset.map(tokenize_function, batched=True)
test_dataset = test_dataset.map(tokenize_function, batched=True)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;klue/roberta-base는 바디만 있는 모델인데, 이를 AutoModelForSequenceClassification로 불러오면 분류 헤드 부분이 랜덤으로 초기화됩니다. 따라서 분류 헤드의 분류 클래스 수를 지정하기 위해 num_labels에 데이터셋의 레이블 수를 지정해줬습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739020903209&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;training_args = TrainingArguments(
    output_dir=&quot;./results&quot;,
    num_train_epochs=1,
    per_device_train_batch_size=8,
    per_device_eval_batch_size=8,
    evaluation_strategy=&quot;epoch&quot;,
    learning_rate=5e-5,
    push_to_hub=False
)

def compute_metrics(eval_pred):
    logits, labels = eval_pred
    predictions = np.argmax(logits, axis=-1)
    return {&quot;accuracy&quot;: (predictions == labels).mean()}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기 코드 부분이 이해가 잘 되지 않았기에 자세하게 설명하고 넘어가도록 하겠습니다. 코드별 상세 설명은 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;output_dir : 결과를 저장할 디렉토리 위치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;num_train_epochs : 학습할 에포크 수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;per_device_train_batch_size : 학습 시 디바이스당 배치 크기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;per_device_eval_batch_size : 평가 시 디바이스당 배치 크기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;,evaluation_strategy=&quot;epoch&quot; : 평가 전략, 여기서는 매 에포크마다 평가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;learning_rate=5e-5 : 학습률&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;push_to_hub=False : 모델을 Hugging Face Hub에 푸시할지 여부&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;eval_pred : 평가 예측값과 실제 레이블을 포함하는 튜플 (logits, labels)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;logits : 모델의 예측값&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;labels : 실제 레이블&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;predictions : logits에서 가장 높은 값을 가진 인덱스를 예측값으로 반환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;accuracy : 예측값과 실제 레이블이 일치하는 비율을 계산하여 반환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;compute_metric 메서드가 이해가 잘 되지 않았는데, 해당 평가 메트릭이 한번만 실행된다고 하는데, 어떤식으로 평가가 이루어지는가 이해가 잘 되지 않았고, logits와 labels에 값이 어떤 형태로 어떤 값이 들어가는지 잘 모르겠었습니다. 하지만 다음 샘플 코드를 통해 완벽하게 이해했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739021470915&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import numpy as np


# 샘플 데이터 (검증 데이터셋 크기: 1000개, 클래스 수: 6개)
logits = np.random.rand(1000, 6)  # 1000개의 샘플, 6개의 클래스에 대한 예측값
labels = np.random.randint(0, 6, size=1000)  # 1000개의 실제 레이블 (0, 1, 2, 3, 4, 5 중 하나)

# logits
logits = np.array([
    [0.1, 0.2, 0.3, 0.4, 0.5, 0.6],  # 첫 번째 샘플의 예측값
    [0.6, 0.5, 0.4, 0.3, 0.2, 0.1],  # 두 번째 샘플의 예측값
    [0.1, 0.3, 0.5, 0.7, 0.9, 0.2],  # 세 번째 샘플의 예측값
    # ... (997개의 샘플 더)
])

# labels
labels = np.array([5, 0, 4, ...])  # 1000개의 실제 레이블&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 지정했던 레이블 수를 기준으로 각 데이터 요소별로 어떤 레이블에 해당하는지 예측값을 지정하고 배열형태로 담습니다. 그 후 labels에 저장된 정답 레이블과 비교를 하여 mean 메소드를 통해 일치하면 True, 그렇지 않으면 False로 이루어진 불리언 배열에서 True의 비율을 계산하여 최종 정확도를 반환하는 구조였습니다. 결국 정확도는 예측값인 logits와 실제 레이블값인 labels의 일치 비율을 나타낸 것이었습니다. 최종적인 모델 학습 및 평가 코드는 다음과 같았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739021673692&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset,
    eval_dataset=valid_dataset,
    tokenizer=tokenizer,
    compute_metrics=compute_metrics,
)

trainer.train()

trainer.evaluate(test_dataset)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 84%의 정확도를 얻을 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1190&quot; data-origin-height=&quot;394&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JcNN0/btsMa6UMLQs/x3k8WL2KrBPNcV2Bi0biX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JcNN0/btsMa6UMLQs/x3k8WL2KrBPNcV2Bi0biX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JcNN0/btsMa6UMLQs/x3k8WL2KrBPNcV2Bi0biX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJcNN0%2FbtsMa6UMLQs%2Fx3k8WL2KrBPNcV2Bi0biX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1190&quot; height=&quot;394&quot; data-origin-width=&quot;1190&quot; data-origin-height=&quot;394&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM을 활용한 실전 AI 애플리케이션 개발 - 허정준&lt;/p&gt;</description>
      <category>AI/DeepLearning</category>
      <category>Bert</category>
      <category>Deeplearning</category>
      <category>test</category>
      <category>tokenizer</category>
      <category>torch</category>
      <category>train</category>
      <category>transformers</category>
      <category>딥러닝</category>
      <category>모델 학습</category>
      <category>카테고리 분류</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/68</guid>
      <comments>https://striver.tistory.com/entry/DeepLearning-%EC%B9%B4%ED%85%8C%EA%B3%A0%EB%A6%AC-%EB%B6%84%EB%A5%98-%EB%AA%A8%EB%8D%B8#entry68comment</comments>
      <pubDate>Sun, 9 Feb 2025 00:24:36 +0900</pubDate>
    </item>
    <item>
      <title>[RAG] gemini api error</title>
      <link>https://striver.tistory.com/entry/RAG-gemini-api-error</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 google의 gemini model을 활용해 Rag를 구현한 코드가 있는데, 오랜만에 실행시켜보니 에러가 발생해서 원인과 해결과정을 정리해 보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류가 작성한 코드는 아래와 같았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1737721993806&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langchain.chains.query_constructor.base import (
    StructuredQueryOutputParser,
    get_query_constructor_prompt,
)
from langchain_openai import ChatOpenAI
from langchain_google_genai import ChatGoogleGenerativeAI

llm = ChatGoogleGenerativeAI(temperature=0, model=&quot;gemini-1.5-flash&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크게 어려운 내용은 없고, model을 지정하는 코드였습니다. 그런데 아래 에러가 발생했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1530&quot; data-origin-height=&quot;603&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wn0GG/btsL2bG27AZ/I7FA0kx2Ksd0SZdTkWCsk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wn0GG/btsL2bG27AZ/I7FA0kx2Ksd0SZdTkWCsk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wn0GG/btsL2bG27AZ/I7FA0kx2Ksd0SZdTkWCsk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwn0GG%2FbtsL2bG27AZ%2FI7FA0kx2Ksd0SZdTkWCsk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1530&quot; height=&quot;603&quot; data-origin-width=&quot;1530&quot; data-origin-height=&quot;603&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPT에게 물어보니, 이 에러는 Google Cloud Application Default Credentials (ADC)와 관련된 문제였습니다. 간단히 말해, Google Cloud API를 사용하려면 애플리케이션이 인증을 통해 권한을 가져야 하는데, 이를 위한 기본 인증 자격 증명(Default Credentials)이 설정되지 않았기 때문에 발생한 것입니다. ADC는 일반적으로 Google Cloud SDK를 통해 설정된 기본 자격 증명을 사용하는데 &quot;gcloud auth application-default login&quot; 명령어를 실행하지 않았거나, gcloud CLI가 제대로 설치되지 않은 경우 에러가 발생할 수 있다고해서 다 설치를 해주도록 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1737722301205&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;1. brew install --cask google-cloud-sdk
2. gcloud init
3. gcloud auth application-default login&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령을 순차적으로 실행해주니 ADC 관련 오류가 해결되 고 코드가 정상적으로 실행되었습니다. 그런데 다른 코드에서 또 에러가 발생하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1365&quot; data-origin-height=&quot;555&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sg3gs/btsL1E304kM/LDmkeRhHYjGYkTeaLs2dEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sg3gs/btsL1E304kM/LDmkeRhHYjGYkTeaLs2dEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sg3gs/btsL1E304kM/LDmkeRhHYjGYkTeaLs2dEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fsg3gs%2FbtsL1E304kM%2FLDmkeRhHYjGYkTeaLs2dEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1365&quot; height=&quot;555&quot; data-origin-width=&quot;1365&quot; data-origin-height=&quot;555&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 에러는 Google Generative AI API에 요청을 보낼 때 인증 토큰의 인증 스코프(authentication scope)가 불충분하기 때문에 발생한 것이었습니다. 찾아보니 Google Cloud는 프로젝트 단위로 리소스와 API 사용을 관리하기 때문에, Google Gemini API를 사용하려면 Google Cloud 프로젝트를 반드시 생성하고 지정해줘야했습니다. 현재 제가 지정한 프로젝트가 있는지 &quot;gcloud config list&quot;로 확인해보았고 지정된 프로젝트가 없는걸 확인할 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;299&quot; data-origin-height=&quot;88&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUwLvQ/btsL2fCErGZ/l9iCEVjeENi6w0FpnemCU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUwLvQ/btsL2fCErGZ/l9iCEVjeENi6w0FpnemCU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUwLvQ/btsL2fCErGZ/l9iCEVjeENi6w0FpnemCU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUwLvQ%2FbtsL2fCErGZ%2Fl9iCEVjeENi6w0FpnemCU1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;299&quot; height=&quot;88&quot; data-origin-width=&quot;299&quot; data-origin-height=&quot;88&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 우선 프로젝트를 지정해주었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1737724703170&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;gcloud config set project [PROJECT_ID]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 다시 확인해보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;296&quot; data-origin-height=&quot;102&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GRfeh/btsL1hussyA/09oLfUtEEznThM4jMLo001/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GRfeh/btsL1hussyA/09oLfUtEEznThM4jMLo001/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GRfeh/btsL1hussyA/09oLfUtEEznThM4jMLo001/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGRfeh%2FbtsL1hussyA%2F09oLfUtEEznThM4jMLo001%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;296&quot; height=&quot;102&quot; data-origin-width=&quot;296&quot; data-origin-height=&quot;102&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트가 잘 지정된 모습을 볼 수 있었습니다. 하지만 이후에도 같은 에러가 발생했고 몇가지 추가 작업을 거쳐야했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. Google Cloud Consol 접속 및 로그인&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 지정한 프로젝트에 접근하여 API 및 서비스 -&amp;gt; 라이브러리 -&amp;gt; &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;Generative Language API&lt;span&gt;&amp;nbsp;활성화&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1582&quot; data-origin-height=&quot;544&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bD45qX/btsL0nWSwpB/41nd3sQ4TO7y4PHffEVKLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bD45qX/btsL0nWSwpB/41nd3sQ4TO7y4PHffEVKLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bD45qX/btsL0nWSwpB/41nd3sQ4TO7y4PHffEVKLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbD45qX%2FbtsL0nWSwpB%2F41nd3sQ4TO7y4PHffEVKLk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1582&quot; height=&quot;544&quot; data-origin-width=&quot;1582&quot; data-origin-height=&quot;544&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;&lt;span&gt;3. 사용자 인증 정보에서 제한사항 목록에 &quot;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;Generative Language API&quot;를 포함&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;664&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tw4o4/btsL016vJ7E/d6tWUhmV6YFXSZabyaHzR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tw4o4/btsL016vJ7E/d6tWUhmV6YFXSZabyaHzR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tw4o4/btsL016vJ7E/d6tWUhmV6YFXSZabyaHzR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ftw4o4%2FbtsL016vJ7E%2Fd6tWUhmV6YFXSZabyaHzR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;890&quot; height=&quot;664&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;664&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;&lt;span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;4. 서비스 계정 생성 및 json 키 발급&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1012&quot; data-origin-height=&quot;564&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bunVTr/btsL0pNZskF/XEFGILaLhRTCZtg2qKaYek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bunVTr/btsL0pNZskF/XEFGILaLhRTCZtg2qKaYek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bunVTr/btsL0pNZskF/XEFGILaLhRTCZtg2qKaYek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbunVTr%2FbtsL0pNZskF%2FXEFGILaLhRTCZtg2qKaYek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1012&quot; height=&quot;564&quot; data-origin-width=&quot;1012&quot; data-origin-height=&quot;564&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5. json key 저장 및 환경 변수 설정&lt;/h4&gt;
&lt;pre id=&quot;code_1737725225554&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export GOOGLE_APPLICATION_CREDENTIALS=&quot;{경로}&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹시 몰라 .env 파일에도 위의 경로르 지정해줍니다. 그 후, &quot;echo $GOOGLE_APPLICATION_CREDENTIALS&quot; 명령어로 json key 파일 경로가 정상 출력되는걸 확인하고 코드를 재실행해주면, 정상적으로 gemini api 가 호출 되는걸 볼 수 있습니다.&lt;/p&gt;</description>
      <category>LLM Engineering/RAG</category>
      <category>Gemini</category>
      <category>Google</category>
      <category>google cloud consol</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/66</guid>
      <comments>https://striver.tistory.com/entry/RAG-gemini-api-error#entry66comment</comments>
      <pubDate>Fri, 24 Jan 2025 22:29:39 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] s3에 이미지 업로드</title>
      <link>https://striver.tistory.com/entry/AWS-s3%EC%97%90-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%97%85%EB%A1%9C%EB%93%9C</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 이미지 추출&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 이미지 업로드&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 이미지 클라이언트에게 전달&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 포스팅에서 만들었던 s3를 활용해 자취방 이미지들을 s3에 업로드해서 사용해보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 이미지 추출&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 pdf에서 이미지를 추출해보도록하겠습니다. 전에 upstage의 document parser를 사용해봤었는데 이미지만 추출하는게 아니라 텍스트,이미지 등을 종류별로 추출해서 마크다운, html 형식으로 변환해주다보니 이미지 추출쪽에서는 성능이 부족한 상황을 경험했습니다. 따라서 이번엔 이미지 추출만을 목적으로 하는 라이브러리를 사용해서 성능에 중점을 맞추고 진행해보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1 ) PyMuPDF&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PyMuPDF 는 PDF에 포함된 원본 이미지 파일을 &lt;b&gt;별도의 변환 없이 추출&lt;/b&gt;합니다. 변환 과정이 없기 때문에 &lt;b&gt;속도&lt;/b&gt;와 &lt;b&gt;품질&lt;/b&gt; 모두 우수합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1735887194400&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import fitz  # PyMuPDF
import os

def extract_images_from_pdf(pdf_path, output_dir):
    # PDF 열기
    pdf_document = fitz.open(pdf_path)
    os.makedirs(output_dir, exist_ok=True)
    image_paths = []

    for page_num in range(len(pdf_document)):
        page = pdf_document[page_num]
        images = page.get_images(full=True)  # 이미지 정보 가져오기

        for img_index, img in enumerate(images):
            xref = img[0]  # 이미지 xref 값
            base_image = pdf_document.extract_image(xref)  # 이미지 추출
            image_bytes = base_image[&quot;image&quot;]
            image_ext = base_image[&quot;ext&quot;]  # 이미지 확장자 (png, jpeg 등)
            image_path = os.path.join(output_dir, f&quot;{page_num + 1}_{img_index + 1}.{image_ext}&quot;)

            # 이미지 파일 저장
            with open(image_path, &quot;wb&quot;) as image_file:
                image_file.write(image_bytes)
            image_paths.append(image_path)
    
    pdf_document.close()
    return image_paths

# PDF 경로와 이미지 저장 디렉토리
pdf_path = './data/pdf_data/back_gate.pdf'
output_dir = './data/images/back_gate'

# 이미지 추출
images = extract_images_from_pdf(pdf_path, output_dir)
print(f&quot;Extracted images: {images}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트용으로 후문에 위치한 자취방들을 대상으로 이미지 추출을 진행해주었습니다. 저장할 이미지명은 &quot;(page_num)_(img_index)&quot; 형식으로 지정했습니다. 후에 page_num으로 이미지를 구분해서 s3에 page_num별로 폴더를 만드려고 위와 같은 형식으로 이미지를 저장했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1330&quot; data-origin-height=&quot;560&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oJ3Vr/btsLDfK3aK7/9NeX3DRMOCCzi1d1uoZebk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oJ3Vr/btsLDfK3aK7/9NeX3DRMOCCzi1d1uoZebk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oJ3Vr/btsLDfK3aK7/9NeX3DRMOCCzi1d1uoZebk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoJ3Vr%2FbtsLDfK3aK7%2F9NeX3DRMOCCzi1d1uoZebk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1330&quot; height=&quot;560&quot; data-origin-width=&quot;1330&quot; data-origin-height=&quot;560&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상당히 괜찮은 화질로 이미지가 추출된걸 확인하고 이제 s3에 이미지를 저장하는 작업을 진행하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 이미지 업로드&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1735887430208&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import boto3
import os
import re

# S3 클라이언트 생성
s3 = boto3.client('s3')
BUCKET_NAME = '{버킷명}'
BASE_FOLDER = 'rooms/'  # S3 내 기본 폴더

def upload_images_to_s3(folder_path):
    # 폴더 내 파일 가져오기
    files = os.listdir(folder_path)

    # 정규식으로 파일 이름에서 room_id와 이미지 번호 추출
    pattern = r&quot;(\d+)_(\d+)\.(\w+)&quot;  # ex: 1_1.jpg, 2_2.png

    for file_name in files:
        match = re.match(pattern, file_name)
        if match:
            room_id, img_num, extension = match.groups()
            room_folder = f&quot;{BASE_FOLDER}{room_id}/&quot;  # S3의 room_id 폴더 경로
            s3_key = f&quot;{room_folder}{file_name}&quot;  # S3에 업로드할 전체 경로

            # 로컬 파일 경로
            local_path = os.path.join(folder_path, file_name)

            # S3에 파일 업로드
            s3.upload_file(local_path, BUCKET_NAME, s3_key)
            print(f&quot;Uploaded {file_name} to s3://{BUCKET_NAME}/{s3_key}&quot;)

# 로컬 이미지 폴더 경로
local_folder_path = './data/images/back_gate'

# 이미지 업로드 실행
upload_images_to_s3(local_folder_path)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 말했던 대로 이미지명에서 room_id로 사용할 이름을 정규표현식으로 추출해서 s3에 room_id 폴더를 만들고 내부에 이미지들을 저장해주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1571&quot; data-origin-height=&quot;666&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eqncxt/btsLCZn7KHd/eTzYRoY3qVxd1mP7RalPck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eqncxt/btsLCZn7KHd/eTzYRoY3qVxd1mP7RalPck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eqncxt/btsLCZn7KHd/eTzYRoY3qVxd1mP7RalPck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feqncxt%2FbtsLCZn7KHd%2FeTzYRoY3qVxd1mP7RalPck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1571&quot; height=&quot;666&quot; data-origin-width=&quot;1571&quot; data-origin-height=&quot;666&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;409&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HwoFr/btsLDfK3qKz/e5dnRbxBRBVa5jyfxwDTt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HwoFr/btsLDfK3qKz/e5dnRbxBRBVa5jyfxwDTt0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HwoFr/btsLDfK3qKz/e5dnRbxBRBVa5jyfxwDTt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHwoFr%2FbtsLDfK3qKz%2Fe5dnRbxBRBVa5jyfxwDTt0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1620&quot; height=&quot;409&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;409&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상적으로 이미지가 업로드 된 모습입니다. 이제 업로드된 이미지가 잘 가져와지는지 테스트 해보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1735887559709&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import boto3

# S3 클라이언트 생성
s3 = boto3.client('s3')
BUCKET_NAME = '{버킷명}'
BASE_FOLDER = 'rooms/'  # S3 내 기본 폴더 경로

def list_images_in_room(bucket_name, room_id):
    folder_prefix = f&quot;{BASE_FOLDER}{room_id}/&quot;  # room_id에 해당하는 폴더 경로
    images = []

    # S3에서 특정 폴더의 객체 리스트 가져오기
    response = s3.list_objects_v2(Bucket=bucket_name, Prefix=folder_prefix)

    # 객체 리스트에서 파일 경로만 추출
    if 'Contents' in response:
        for obj in response['Contents']:
            images.append(obj['Key'])  # S3 객체 키 (파일 경로)

    return images

# 예시: room_id가 1인 경우
room_id = '1'
images = list_images_in_room(BUCKET_NAME, room_id)
print(f&quot;Images in room {room_id}: {images}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번 room_id에 해당하는 이미지들을 가져오는 샘플 코드를 작성해서 실행해보았고 아래 에러가 발생했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1524&quot; data-origin-height=&quot;95&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GrCGQ/btsLCqTIBA6/5rqU9uGhXQfpgro2BeOkAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GrCGQ/btsLCqTIBA6/5rqU9uGhXQfpgro2BeOkAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GrCGQ/btsLCqTIBA6/5rqU9uGhXQfpgro2BeOkAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGrCGQ%2FbtsLCqTIBA6%2F5rqU9uGhXQfpgro2BeOkAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1524&quot; height=&quot;95&quot; data-origin-width=&quot;1524&quot; data-origin-height=&quot;95&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 에러는 AWS IAM 정책이 S3 버킷에 대해 s3:ListBucket 액션을 허용하지 않아서 발생하는 문제입니다. 현재 사용 중인 IAM 사용자 또는 역할에게 s3:ListBucket 권한이 필요한 것 이었습니다. 이를 해결해주기 위해 s3:ListBucket 권한을 부여해주도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NOvb4/btsLErwW7Eo/T7T8nDNryAHdy1akpX6uM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NOvb4/btsLErwW7Eo/T7T8nDNryAHdy1akpX6uM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NOvb4/btsLErwW7Eo/T7T8nDNryAHdy1akpX6uM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNOvb4%2FbtsLErwW7Eo%2FT7T8nDNryAHdy1akpX6uM0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1620&quot; height=&quot;494&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;494&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;AmazonS3FullAccess&quot; 를 추가해서 s3 에 대한 모든 요청을 허용하도록 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;41&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lIiPX/btsLBvVxoNT/onDQWbJJe801q01dWoJmn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lIiPX/btsLBvVxoNT/onDQWbJJe801q01dWoJmn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lIiPX/btsLBvVxoNT/onDQWbJJe801q01dWoJmn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlIiPX%2FbtsLBvVxoNT%2FonDQWbJJe801q01dWoJmn1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;920&quot; height=&quot;41&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;41&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 다시 실행해보니 정상적으로 이미지가 가져와지는 모습을 볼 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 이미지 클라이언트에게 전달&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지정한 room_id에 맞는 이미지들을 추출할 수 있지만 위의 형태로는 클라이언트에게 이미지를 보여줄 수 없습니다. 클라이언트에서 room_id로 요청이 들어오면 해당 이미지들에 대한 객체 URL을 json format으로 바꿔서 클라이언트에게 전달해 주도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 URL은 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;&quot;https://&amp;lt;bucket-name&amp;gt;.s3.&amp;lt;region&amp;gt;.amazonaws.com/&amp;lt;object-key&amp;gt;&quot;&lt;/b&gt;&lt;/u&gt;&lt;/span&gt; 의 형태를 지니고 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1735901947466&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import boto3
import json

# S3 클라이언트 생성
s3 = boto3.client('s3')
BUCKET_NAME = '{버킷명}'
BASE_FOLDER = 'rooms/'  # S3 내 기본 폴더 경로
REGION = 'ap-northeast-2'  # S3 버킷이 위치한 AWS 리전

def list_images_in_room(bucket_name, room_id):
    folder_prefix = f&quot;{BASE_FOLDER}{room_id}/&quot;  # room_id에 해당하는 폴더 경로
    images = []

    # S3에서 특정 폴더의 객체 리스트 가져오기
    response = s3.list_objects_v2(Bucket=bucket_name, Prefix=folder_prefix)

    # 객체 리스트에서 파일 경로만 추출
    if 'Contents' in response:
        for obj in response['Contents']:
            if not obj['Key'].endswith('/'):  # 폴더 자체는 제외
                images.append(obj['Key'])  # S3 객체 키 (파일 경로)

    return images

def generate_image_urls(bucket_name, region, keys):
    urls = [
        f&quot;https://{bucket_name}.s3.{region}.amazonaws.com/{key}&quot;
        for key in keys
    ]
    return urls

# 예시: room_id가 1인 경우
room_id = '1'
keys = list_images_in_room(BUCKET_NAME, room_id)

# S3 객체 키를 URL로 변환
image_urls = generate_image_urls(BUCKET_NAME, REGION, keys) if keys else []

# JSON 응답 생성
response_data = {
    &quot;room_id&quot;: room_id,
    &quot;images&quot;: image_urls
}
response_json = json.dumps(response_data, indent=4)  # JSON 문자열로 변환 (보기 좋게 들여쓰기 포함)

# 클라이언트에게 전송 (예: 출력)
print(response_json)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 위와 같이 코드를 작성해서 image가 있는 room_id 요청에 대해서는 적절한 이미지들을 json 으로 전달하고, 이미지가 없는 경우 비어있는 images를 전달하게끔 코드를 작성하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1059&quot; data-origin-height=&quot;255&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qc6nX/btsLDVSM7mn/ln7dkFbCOaXBcqJxKKtAxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qc6nX/btsLDVSM7mn/ln7dkFbCOaXBcqJxKKtAxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qc6nX/btsLDVSM7mn/ln7dkFbCOaXBcqJxKKtAxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fqc6nX%2FbtsLDVSM7mn%2Fln7dkFbCOaXBcqJxKKtAxK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1059&quot; height=&quot;255&quot; data-origin-width=&quot;1059&quot; data-origin-height=&quot;255&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Proejcts/CampusMeet</category>
      <category>pdf 이미지 추출</category>
      <category>S3</category>
      <category>버킷</category>
      <category>이미지 추출</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/64</guid>
      <comments>https://striver.tistory.com/entry/AWS-s3%EC%97%90-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%97%85%EB%A1%9C%EB%93%9C#entry64comment</comments>
      <pubDate>Fri, 3 Jan 2025 20:02:29 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] s3</title>
      <link>https://striver.tistory.com/entry/AWS-s3</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. s3 용어&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. s3 버킷 생성&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 진행도중 이미지를 저장하고 관리해야할 일이 생겨서 AWS의 s3 시스템을 사용해보도록 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. &lt;span style=&quot;background-color: #fcfcfc; text-align: left;&quot;&gt;s3 용어&lt;/span&gt; &lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1) 버킷&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체를 저장하는 공간&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2) 객체&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지나 동영상 같은 파일&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3) 버킷명&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유일한 이름&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;4) 객체 키&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 식별자 (모든 객체가 하나씩 가짐)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. s3 버킷 생성&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본격적으로 aws 콘솔에 접속해서 버킷을 생성해보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1309&quot; data-origin-height=&quot;819&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bY26Gt/btsLCExdOvH/0pq0S9xVItWtQldzoP5a01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bY26Gt/btsLCExdOvH/0pq0S9xVItWtQldzoP5a01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bY26Gt/btsLCExdOvH/0pq0S9xVItWtQldzoP5a01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbY26Gt%2FbtsLCExdOvH%2F0pq0S9xVItWtQldzoP5a01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1309&quot; height=&quot;819&quot; data-origin-width=&quot;1309&quot; data-origin-height=&quot;819&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;s3 서비스는 사용한만큼 요금이 지불되기 때문에 현재 사용중인 프리티어 기준에 맞게 이미지 용량을 사용할 생각입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yWElb/btsLD16k8Qp/NoDLDXZfZCva6QHRCJx9R0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yWElb/btsLD16k8Qp/NoDLDXZfZCva6QHRCJx9R0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yWElb/btsLD16k8Qp/NoDLDXZfZCva6QHRCJx9R0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyWElb%2FbtsLD16k8Qp%2FNoDLDXZfZCva6QHRCJx9R0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1684&quot; height=&quot;640&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;640&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버킷 이름 규칙에 맞게 이름을 지정하고 넘어가줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1662&quot; data-origin-height=&quot;812&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmFEbn/btsLDlqcZjX/yXperSS6uUwLRqbaykHod0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmFEbn/btsLDlqcZjX/yXperSS6uUwLRqbaykHod0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmFEbn/btsLDlqcZjX/yXperSS6uUwLRqbaykHod0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmFEbn%2FbtsLDlqcZjX%2FyXperSS6uUwLRqbaykHod0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1662&quot; height=&quot;812&quot; data-origin-width=&quot;1662&quot; data-origin-height=&quot;812&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 값들은 추가로 건들이지않고 테스트를 위해 태그를 하나 생성해주었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1714&quot; data-origin-height=&quot;484&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ocspc/btsLDN1AOko/T4E4nEB2yq1bn1Hy3r88SK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ocspc/btsLDN1AOko/T4E4nEB2yq1bn1Hy3r88SK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ocspc/btsLDN1AOko/T4E4nEB2yq1bn1Hy3r88SK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOcspc%2FbtsLDN1AOko%2FT4E4nEB2yq1bn1Hy3r88SK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1714&quot; height=&quot;484&quot; data-origin-width=&quot;1714&quot; data-origin-height=&quot;484&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버킷이 잘 만들어진 모습을 볼 수 있습니다. 이제 버킷을 들어가서 이미지를 업로드 해주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;887&quot; data-origin-height=&quot;154&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PCWFp/btsLD0FSWoD/Ep3xWDZMFoaJxP4gh0KyiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PCWFp/btsLD0FSWoD/Ep3xWDZMFoaJxP4gh0KyiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PCWFp/btsLD0FSWoD/Ep3xWDZMFoaJxP4gh0KyiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPCWFp%2FbtsLD0FSWoD%2FEp3xWDZMFoaJxP4gh0KyiK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;887&quot; height=&quot;154&quot; data-origin-width=&quot;887&quot; data-origin-height=&quot;154&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 업로드는 잘 됐지만 url로 들어가보면 위와 같은 에러가 발생하는 모습이었습니다. 원인은 버킷 정책을 지정해주지않아서 이미지를 읽어오는 과정에서 문제가 생긴 것 이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;668&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbbuxb/btsLBLRc0yd/QVjBLIT3gYew8kHkMDK6V0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbbuxb/btsLBLRc0yd/QVjBLIT3gYew8kHkMDK6V0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbbuxb/btsLBLRc0yd/QVjBLIT3gYew8kHkMDK6V0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbbbuxb%2FbtsLBLRc0yd%2FQVjBLIT3gYew8kHkMDK6V0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1620&quot; height=&quot;668&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;668&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버킷 정책에 들어가서 편집을 선택합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1722&quot; data-origin-height=&quot;1292&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chk9Zb/btsLEgvrZj4/ddKKQcJio3X13z0ldEXaAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chk9Zb/btsLEgvrZj4/ddKKQcJio3X13z0ldEXaAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chk9Zb/btsLEgvrZj4/ddKKQcJio3X13z0ldEXaAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fchk9Zb%2FbtsLEgvrZj4%2FddKKQcJio3X13z0ldEXaAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1722&quot; height=&quot;1292&quot; data-origin-width=&quot;1722&quot; data-origin-height=&quot;1292&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면과 같이 값을 입력해서 json 형태의 정책을 생성했습니다. 여기서 주의할점은 Resource의 나의 버킷 ARN 뒤에 (/*)을 꼭 붙혀줘야한다는 것입니다. 그렇지 않으면 아래와같은 에러가 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1610&quot; data-origin-height=&quot;176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cioGeX/btsLCKxdEiM/xGbZTpuv1MkOU1EkKGkalk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cioGeX/btsLCKxdEiM/xGbZTpuv1MkOU1EkKGkalk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cioGeX/btsLCKxdEiM/xGbZTpuv1MkOU1EkKGkalk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcioGeX%2FbtsLCKxdEiM%2FxGbZTpuv1MkOU1EkKGkalk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1610&quot; height=&quot;176&quot; data-origin-width=&quot;1610&quot; data-origin-height=&quot;176&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1648&quot; data-origin-height=&quot;1052&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XOifR/btsLBYiTcfo/GNu4vXRsIsF5VJkcSnZmq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XOifR/btsLBYiTcfo/GNu4vXRsIsF5VJkcSnZmq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XOifR/btsLBYiTcfo/GNu4vXRsIsF5VJkcSnZmq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXOifR%2FbtsLBYiTcfo%2FGNu4vXRsIsF5VJkcSnZmq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1648&quot; height=&quot;1052&quot; data-origin-width=&quot;1648&quot; data-origin-height=&quot;1052&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제&amp;nbsp; 잘 생성된 json 형태의 정책을 복사하여 붙혀넣었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1754&quot; data-origin-height=&quot;246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqx7dR/btsLCHgWx8y/KOfK3PO43JNPq2QWNbsoUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqx7dR/btsLCHgWx8y/KOfK3PO43JNPq2QWNbsoUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqx7dR/btsLCHgWx8y/KOfK3PO43JNPq2QWNbsoUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbqx7dR%2FbtsLCHgWx8y%2FKOfK3PO43JNPq2QWNbsoUk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1754&quot; height=&quot;246&quot; data-origin-width=&quot;1754&quot; data-origin-height=&quot;246&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 다른 에러가 발생했습니다. 찾아보니 처음 버킷을 생성할때 퍼빌릭 액세스를 모두 차단하면서 생긴 에러였습니다. 역할을 지정하던가 다른 방법을 사용해서 퍼블릭 액세스 차단을 유지할 수 있지만 우선 지금은 퍼블릭 액세스 차단을 모두 해제하고 넘어가도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1623&quot; data-origin-height=&quot;498&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDTkJ9/btsLEcF8SQg/y3R9OUUG3sp4LUkrOnGjmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDTkJ9/btsLEcF8SQg/y3R9OUUG3sp4LUkrOnGjmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDTkJ9/btsLEcF8SQg/y3R9OUUG3sp4LUkrOnGjmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDTkJ9%2FbtsLEcF8SQg%2Fy3R9OUUG3sp4LUkrOnGjmK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1623&quot; height=&quot;498&quot; data-origin-width=&quot;1623&quot; data-origin-height=&quot;498&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 차단 되어있던부분을 모두 해제해 주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1720&quot; data-origin-height=&quot;874&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Gqqn9/btsLC10zftP/Hxh1QtEQ9E2iiguto3A6M1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Gqqn9/btsLC10zftP/Hxh1QtEQ9E2iiguto3A6M1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Gqqn9/btsLC10zftP/Hxh1QtEQ9E2iiguto3A6M1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGqqn9%2FbtsLC10zftP%2FHxh1QtEQ9E2iiguto3A6M1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1720&quot; height=&quot;874&quot; data-origin-width=&quot;1720&quot; data-origin-height=&quot;874&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정책이 정상적으로 편집된 모습을 볼 수 있었습니다. 이제 이미지 링크를 타고 들어가면 s3 에 저장된 이미지를 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://growth-coder.tistory.com/114&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://growth-coder.tistory.com/114&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Infra/AWS</category>
      <category>Bucket</category>
      <category>S3</category>
      <category>버킷</category>
      <category>정책</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/63</guid>
      <comments>https://striver.tistory.com/entry/AWS-s3#entry63comment</comments>
      <pubDate>Fri, 3 Jan 2025 01:21:01 +0900</pubDate>
    </item>
    <item>
      <title>[Backend] Access Token &amp;amp; Refresh Token</title>
      <link>https://striver.tistory.com/entry/Backend-Access-Token-Refresh-Token</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1.&amp;nbsp; Access Token과 Refresh Token의 필요성&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. JWT 인증 체계 설계&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 결론&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이전 블로그에서 JWT에 대해서 알아보았습니다. 그렇다면, 이러한 JWT가 일반적으로 많이 사용되는 곳이 어디일까?를 생각해보면 &lt;b&gt;인증 체계&lt;/b&gt;였습니다. JWT인 &lt;b&gt;Access Token&lt;/b&gt;과 &lt;b&gt;Refresh Token&lt;/b&gt;을 만들고 이를 이용해 인증 체계를 구축한 과정에 대해 말씀드리도록 하겠습니다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1.&amp;nbsp; Access Token과 Refresh Token의 필요성&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Access Token 하나만 가지고도 인증할 수 있지 않을까? 라는 생각이 들 수 있지만, 실제로는 하나의 토큰으로는 여러 문제점들이 발생할 수 있습니다. Access Token만 사용한다고 가정해보겠습니다. Payload에 사용자 정보를 담아 통신 간에 JWT를 사용할 것입니다. 서버는 전달받은 Access Token을 디코딩하고 저장된 Secret Key를 이용해 유효성을 검증함으로써 인증을 수행할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 만약 이 Access Token이 한번 탈취되었다고 가정해봅시다. 서버는 이 Access Token이 정상적인 클라이언트에서 온 것인지, 아니면 탈취된 악의적인 이용자로부터 온 것인지 구분할 수 없기 때문에 토큰을 무기한으로 갱신하게 될 것입니다. 이는 탈취된 토큰이 장기간 동안 악용될 수 있다는 것을 의미합니다. 또한, Access Token에 사용자 정보가 포함되어 있기 때문에, 탈취된 토큰을 통해 지속적으로 사용자 정보를 악의적으로 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 문제들을 해결하기 위해 등장한 것이 Refresh Token입니다. Refresh Token은 Access Token과 마찬가지로 JWT 형식을 사용하지만, 몇 가지 중요한 차이점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 35.6977%; text-align: center;&quot;&gt;Access Token&lt;/td&gt;
&lt;td style=&quot;width: 39.3023%; text-align: center;&quot;&gt;Refresh Token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;목적&lt;/td&gt;
&lt;td style=&quot;width: 35.6977%; text-align: center;&quot;&gt;인증이 필요한 요청을 처리하는 데 사용됩니다. 필요한 사용자 정보와 권한을 담고 있습니다.&lt;/td&gt;
&lt;td style=&quot;width: 39.3023%; text-align: center;&quot;&gt;Access Token을 재발급 받기 위해 사용됩니다. 불필요한 사용자 정보를 담지 않고, 오직 토큰 재발급에만 관여합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;일반적인 유효기간&lt;/td&gt;
&lt;td style=&quot;width: 35.6977%; text-align: center;&quot;&gt;30분~1시간&lt;/td&gt;
&lt;td style=&quot;width: 39.3023%; text-align: center;&quot;&gt;1주~2주&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 Access Token과 Refresh Token을 함께 사용함으로써 보안을 강화하고, Access Token의 짧은 유효 기간 동안만 인증을 수행할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2.&amp;nbsp; JWT 인증 체계 설계&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1) 인증 체계 설계&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 구성한 인증 체계 로직은 다음과 같습니다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;로그인 과정&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 ID와 비밀번호를 통해 정상적으로 로그인합니다.&lt;/li&gt;
&lt;li&gt;서버는 회원 DB를 조회하여 사용자를 확인합니다.&lt;/li&gt;
&lt;li&gt;로그인에 성공하면 서버는 &lt;b&gt;Access Token&lt;/b&gt;과 &lt;b&gt;Refresh Token&lt;/b&gt;을 생성하여 클라이언트에게 발급하고, Redis에 Refresh Token을 저장합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;토큰 저장&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트는 전달받은 토큰들을 안전한 로컬 저장소(예: Secure Storage)에 저장합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인증 요청&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트는 인증이 필요한 API 요청 시마다 &lt;b&gt;Access Token&lt;/b&gt;을 헤더에 담아 서버로 전송합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Access Token 검증&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버는 전달받은 Access Token을 검증하여 유효한 경우 요청에 알맞은 데이터를 응답합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Access Token 만료 처리&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시간이 지나 Access Token이 만료되면, 클라이언트는 만료된 Access Token을 사용해 API 요청을 보냅니다.&lt;/li&gt;
&lt;li&gt;서버는 만료된 Access Token을 검증하고, 만료되었다는 응답을 클라이언트에게 반환합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;토큰 갱신 요청&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트는 만료된 Access Token과 저장된 Refresh Token을 함께 헤더에 담아 &lt;b&gt;Access Token 재발급 API&lt;/b&gt;를 호출합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;토큰 검증 및 재발급&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버는 받은 Access Token이 변조되지 않았는지 검증합니다.&lt;/li&gt;
&lt;li&gt;Refresh Token을 Redis에 저장된 토큰과 비교하여 동일하고 유효기간이 지나지 않았다면, 새로운 Access Token과 Refresh Token을 발급하고 Redis에 업데이트합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;새 토큰 저장 및 재요청&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트는 새로 발급받은 토큰들을 다시 로컬 저장소에 저장하고, 새로운 Access Token을 사용해 원래의 API 요청을 재전송합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2) 설계 과정&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JWT를 이용한 인증 체계 구축은 Access Token과 Refresh Token을 사용하는 공통점이 있지만, 전달할 토큰의 종류, 저장 위치 등 여러 방식으로 구현할 수 있습니다. 제가 위와 같은 인증 체계를 구축하게 된 근거와 과정에서 겪었던 궁금증들에 대해 설명드리겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;(1) Access Token 만료 시 바로 재발급 가능 여부&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트가 만료된 Access Token으로 API를 호출하면, 서버는 에러 코드를 반환한 뒤 클라이언트가 Refresh Token을 사용해 Access Token 갱신 API를 호출하도록 유도합니다. 그런데, 서버에서 바로 저장된 Refresh Token을 디코딩해 유저 정보를 확인한 뒤 Access Token을 재발급하면, 불필요한 API 호출을 줄일 수 있지 않을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 이 질문은 JWT에 대한 이해도 부족으로 생겼던 궁금증이라고 생각합니다. 이론적으로 가능하지만, 일반적인 구현 방식과는 다릅니다. 이유는 다음과 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Payload 차이&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Access Token의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Payload&lt;/b&gt;는 유저 정보(예: 권한, 사용자 ID 등)를 담는 경우가 많습니다.&lt;/li&gt;
&lt;li&gt;Refresh Token의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Payload&lt;/b&gt;는 보안성을 높이기 위해 최소한의 정보(예: 사용자 ID)만 담습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서명 불일치&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Access Token과 Refresh Token은 서로 다른 Payload를 가지며, 서명(Signature)도 각각 다릅니다.&lt;/li&gt;
&lt;li&gt;서버는 Refresh Token으로 Access Token을 대체하려고 하면 서명 검증에서 실패하여 인증 오류가 발생합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;결론적으로, Refresh Token을 사용한 Access Token 갱신은 별도의 API 호출로 처리하는 것이 일반적이며, 이는 보안성과 구조적 일관성을 유지하기 위해 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;(2) JWT 생성 시 Header 명시 여부&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;아래 코드에서 jwt.encode 호출 시 Header를 명시하지 않았습니다. 이런 경우 JWT가 정상적으로 생성되는가?&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1733993501246&quot; class=&quot;makefile&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;access_token = jwt.encode(
	access_token_payload, self.secret, algorithm=ALGORITHM
)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 처음 FastAPI에서 JWT를 인코딩하려고 할 때, 이론적으로 JWT를 생성할 때 Header, Payload, Signature 세 가지 요소를 명시해야 한다고 공부했습니다. 그러나 jwt.encode 함수에 Payload, Secret Key, Algorithm만 인자로 전달했는데도 JWT가 정상적으로 생성되는 것을 보고 혼동하게 되었습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조사를 해보니, jwt.encode 메서드는 내부적으로 &lt;b&gt;Header&lt;/b&gt;를 자동으로 생성하여 JWT 토큰에 포함시킵니다. 따라서, 코드에서 명시적으로 Header를 지정하지 않아도 algorithm 매개변수에 따라 기본 Header가 생성됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, HS256 알고리즘을 사용할 경우 Header는 다음과 같이 자동으로 생성됩니다:&lt;/p&gt;
&lt;pre id=&quot;code_1733993501247&quot; class=&quot;json&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;alg&quot;: &quot;HS256&quot;,
  &quot;typ&quot;: &quot;JWT&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로, jwt.encode 함수는 Header를 자동으로 생성하고, 이 Header와 Payload를 Secret Key를 이용해 &lt;b&gt;Signature&lt;/b&gt;로 만듭니다. 이 세 요소를 Base64로 인코딩하여 최종적인 JWT를 생성하게 됩니다. 이는 제가 이론적으로 공부한 내용과 일치하지만, encode 함수의 구현 차이로 인해 Header를 별도로 지정할 필요가 없다는 것을 알게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;(3) Refresh Token 없이 Access Token만 사용하는 경우의 문제점&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Access Token의 유효성을 서버에서 Secret Key를 이용해 검증하면 데이터베이스 조회 없이도 인증이 가능합니다. 그렇다면 Refresh Token 없이 Access Token만으로 인증 체계를 구축해도 문제가 없지 않을까요? Refresh Token이 필요한 이유는 무엇인가요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;-&amp;gt; 이 질문 역시 JWT 이론에서 비롯된 오해였습니다. 처음에는 서버에 저장된 Secret Key로 토큰의 유효성을 검증하고, 유효하면 바로 새로운 Access Token을 발급하는 방식으로 생각했으나, 이는 여러 문제를 초래할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Access Token 탈취 위험&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 만료된 액세스 토큰으로 갱신 요청을 보내는 것과 탈취된 만료 액세스 토큰으로 갱신 요청을 보내는 것을 서버는 구분할 수가 없습니다. 즉, 탈취된 Access Token은 만료되기 전까지 악용될 수 있습니다.&lt;/li&gt;
&lt;li&gt;만약 탈취된 Access Token으로 갱신 요청을 하면, 서버는 클라이언트와 공격자를 구분할 수 없어&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;무한 갱신 요청&lt;/b&gt;을 허용하게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Refresh Token의 역할&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Access Token 갱신 시 반드시 Refresh Token을 사용하도록 설계하면, 탈취된 Access Token만으로는 갱신이 불가능합니다.&lt;/li&gt;
&lt;li&gt;Refresh Token은 클라이언트의 안전한 저장소에 보관되며, 상대적으로 탈취 가능성이 낮습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;세션 제어 가능&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Refresh Token을 서버에서 관리하면, 특정 사용자 세션을 강제로 종료하거나 로그아웃 시 모든 세션을 무효화할 수 있습니다.&lt;/li&gt;
&lt;li&gt;반면, Access Token만 사용하는 경우 서버에서 세션을 제어할 방법이 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(4) 인증 요청 시 Access Token만 전송 vs Access Token과 Refresh Token 함께 전송&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증이 필요한 요청을 보낼 때 클라이언트에서 헤더에 Access Token만 담는 경우와 Refresh Token까지 함께 담아서 보내는 경우 중 어떤 방법이 옳을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 각각의 방법에는 장단점이 있어 고민이 많았으나, 저는 &lt;b&gt;Access Token만 헤더에 담아 전송하는 방식을 선택했습니다&lt;/b&gt;. 이유는 다음과 같습니다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;보안성 강화&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Refresh Token을 매 요청마다 전송하지 않음으로써, Refresh Token의 노출 위험을 줄였습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;네트워크 효율성&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반적인 API 요청 시 Refresh Token을 포함하지 않아 네트워크 트래픽을 줄일 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클라이언트 복잡성 감소&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Refresh Token을 별도로 관리하여, 클라이언트 측에서 토큰 갱신 로직을 명확히 분리할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용자 경험&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Access Token이 만료되었을 때, 클라이언트가 자동으로 갱신 API를 호출하여 새로운 토큰을 받음으로써 사용자는 인증 과정의 복잡성을 느끼지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 이유로, &lt;b&gt;Access Token만을 사용하고, 만료 시 Refresh Token을 별도로 사용하는 방식&lt;/b&gt;이 보안과 효율성 면에서 더 적합하다고 판단했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;(5) Refresh Token을 Redis에 저장하는 이유&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 Refresh Token을 Redis에 저장하는 것이지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;-&amp;gt;JWT 인증 체계에서 Refresh Token을 Redis에 저장하는 이유는 &lt;b&gt;서버 부하를 줄이면서도 보안과 세션 관리를 효율적으로 유지&lt;/b&gt;하기 위함입니다. 자세한 이유는 다음과 같습니다:&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;고속 데이터 접근&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Redis는 메모리 기반 저장소로, 데이터 읽기/쓰기가 매우 빠릅니다.&lt;/li&gt;
&lt;li&gt;Refresh Token의 조회와 갱신 작업을 빠르게 처리하여 서버 부하를 최소화할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;효율적인 TTL 관리&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Redis는 각 키에 대해 TTL(Time-To-Live)을 설정할 수 있어, Refresh Token의 만료를 자동으로 관리할 수 있습니다.&lt;/li&gt;
&lt;li&gt;만료된 Refresh Token은 자동으로 삭제되므로, 추가적인 관리 작업이 필요 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;One-Time Token 방식 적용&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Refresh Token을 갱신 요청 시마다 새로운 토큰으로 교체하고 기존 토큰을 삭제함으로써, Refresh Token의 재사용 공격을 방지할 수 있습니다.&lt;/li&gt;
&lt;li&gt;Redis는 이러한 갱신 과정을 빠르게 처리할 수 있어 서버 부하를 크게 줄일 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 이유로, &lt;b&gt;Refresh Token을 Redis에 저장하는 방식&lt;/b&gt;은 보안성과 성능을 모두 만족시키며, 서버 부하를 효과적으로 관리할 수 있는 최적의 방법입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JWT 인증 체계는 &lt;b&gt;Access Token&lt;/b&gt;과 &lt;b&gt;Refresh Token&lt;/b&gt;을 함께 사용함으로써 보안성과 효율성을 동시에 높일 수 있습니다. &lt;b&gt;Access Token&lt;/b&gt;은 짧은 유효 기간으로 인증을 수행하고, &lt;b&gt;Refresh Token&lt;/b&gt;은 장기간 동안 안전하게 토큰을 갱신하는 역할을 합니다. 이를 통해 토큰 탈취 시의 위험을 줄이고, 사용자 세션을 효과적으로 관리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Refresh Token을 Redis와 같은 서버 측 스토리지에 저장함으로써&lt;/b&gt;, 토큰의 유효성을 검증하고 보안을 강화할 수 있습니다. 이러한 인증 체계는 &lt;b&gt;대규모 서비스&lt;/b&gt;나 &lt;b&gt;보안이 중요한 애플리케이션&lt;/b&gt;에서 특히 유용하게 사용됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;(추가 수정사항)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 Refresh Token Rotation 방식으로 구현한 인증 로직을 팀원들과 검토하던 중, 한 가지 의견이 제시되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;의견 제시사항&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;Refresh Token이 한 번 탈취되면, 이를 이용해 계속해서 Access Token과 Refresh Token을 새로 발급받을 수 있는 구조가 아닌가?&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 의견에 동의하게 되었고, 인증 로직을 수정하기로 결정했습니다. 기존 방식에서는 Access Token이 만료될 때마다 Refresh Token을 함께 새롭게 발급했지만, 수정된 로직에서는 Access Token만 새롭게 발급하고, Refresh Token은 계속 재사용하도록 변경하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서 한 가지 우려되었던 점은, &quot;&quot;사용자가 앱을 정상적으로 사용하더라도 Refresh Token의 유효기간이 지나면 강제로 로그인을 다시 해야 하는 상황이 발생하지 않을까?&quot;&quot; 라는 부분이었습니다. 이를 해결하기 위해 앱 실행하면서 자동로그인할때 Access Token과 Refresh Token을 새롭게 발급하도록 로직을 보완하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-Access-Token-Refresh-Token-%EC%9B%90%EB%A6%AC-feat-JWT&quot;&gt;https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-Access-Token-Refresh-Token-%EC%9B%90%EB%A6%AC-feat-JWT&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Dev/Backend</category>
      <category>access token</category>
      <category>jwt</category>
      <category>Redis</category>
      <category>refresh token</category>
      <category>인증</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/61</guid>
      <comments>https://striver.tistory.com/entry/Backend-Access-Token-Refresh-Token#entry61comment</comments>
      <pubDate>Thu, 12 Dec 2024 18:33:17 +0900</pubDate>
    </item>
    <item>
      <title>[Backend] JWT</title>
      <link>https://striver.tistory.com/entry/Backend-JWT</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. JWT (JSON Web Token)란?&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 쿠키 기반 인증 vs JWT&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.&amp;nbsp; JWT (JSON Web Token)란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JWT는 &lt;b&gt;Header, Payload, Signature&lt;/b&gt;로 구성된 토큰 기반 인증 방식입니다. 데이터를 Base64로 인코딩하고, 세 요소를 마침표(.)로 연결하여 하나의 토큰으로 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1 ) JWT 구조&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(1) &lt;b&gt;Header&lt;/b&gt;: 서명 알고리즘과 토큰 타입 정보를 포함.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예: {&quot;alg&quot;: &quot;HS256&quot;, &quot;typ&quot;: &quot;JWT&quot;}&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(2) &lt;b&gt;Payload&lt;/b&gt;: 사용자 정보와 클레임(예: 만료 시간 exp, 사용자 ID sub) 등을 포함.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예: {&quot;sub&quot;: &quot;user123&quot;, &quot;exp&quot;: 1700000000}&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(3) &lt;b&gt;Signature&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;: Header + Payload를 서버의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;Secret Key&lt;/b&gt;로 서명한 값.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예: HMACSHA256(Base64UrlEncode(Header) + &quot;.&quot; + Base64UrlEncode(Payload), SecretKey)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 이 3가지 값들이 BAse64Url로 인코딩 되어 마침표(.)으로 연결함으로써 JWT가 구성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2) JWT 인증 원리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(1) 클라이언트가 서버로부터 JWT를 전달받아 저장.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(2) 클라이언트가 요청 시 JWT를 함께 전송.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(3) 서&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;버는 전달받은 JWT의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;Signature&lt;/b&gt;를 검증하여 토큰이 조작되지 않았음을 확인.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서명 알고리즘, Payload, Secret Key를 이용해 Signature를 생성하고, 전달받은 토큰의 Signature와 비교.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;토큰 검증 과정에서 DB를 조회하지 않아도 되기 때문에 서버 부하를 줄일 수 있음&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.&amp;nbsp; 쿠키 기반 인증 vs JWT&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1) 쿠키 기반 인증&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2254&quot; data-origin-height=&quot;1192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lJUTX/btsLecfBWWN/kmsudPsUguaMIKgxTzIGk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lJUTX/btsLecfBWWN/kmsudPsUguaMIKgxTzIGk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lJUTX/btsLecfBWWN/kmsudPsUguaMIKgxTzIGk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlJUTX%2FbtsLecfBWWN%2FkmsudPsUguaMIKgxTzIGk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2254&quot; height=&quot;1192&quot; data-origin-width=&quot;2254&quot; data-origin-height=&quot;1192&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;브라우저가 로그인을 시도하면 애플리케이션 서버는 DB를 조회해서 회원 유무 확인&lt;/li&gt;
&lt;li&gt;회원 정보가 있다면 Session table에 어떤 사용자가 로그인했고 session id 라고 하는 임시 비밀번호를 발급&lt;/li&gt;
&lt;li&gt;이를 서버가 다시 쿠키값으로 브라우저에게 응답&lt;/li&gt;
&lt;li&gt;이 값은 브라우저에 저장되고 다음에 접속할때마다 서버에 저장됨.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;추가 조회 필요&lt;/b&gt;: Session ID만으로는 사용자의 세부 정보를 알 수 없어 추가적인 DB 조회가 필요.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서버 부하&lt;/b&gt;: 요청마다 Session Table 확인이 필요.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;확장성 부족&lt;/b&gt;: 새로운 장치로 로그인 시 Session Table에 새로운 데이터가 추가되어 관리 부담 증가.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2) JWT&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2262&quot; data-origin-height=&quot;1154&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvgknA/btsLcsR08rY/DcXRqcWm5XKSarKOYi2op0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvgknA/btsLcsR08rY/DcXRqcWm5XKSarKOYi2op0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvgknA/btsLcsR08rY/DcXRqcWm5XKSarKOYi2op0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvgknA%2FbtsLcsR08rY%2FDcXRqcWm5XKSarKOYi2op0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2262&quot; height=&quot;1154&quot; data-origin-width=&quot;2262&quot; data-origin-height=&quot;1154&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;브라우저에서 로그인을 시도하면, 애플리케이션 서버는 데이터베이스를 조회하여 회원 유무를 확인&lt;/li&gt;
&lt;li&gt;회원 정보가 존재하면, 서버는 JWT를 생성하여 브라우저로 전달&lt;/li&gt;
&lt;li&gt;클라이언트는 전달받은 JWT를 쿠키나 로컬스토리지와 같은 저장소에 보관&lt;/li&gt;
&lt;li&gt;클라이언트는 인증이 필요한 서비스 이용 시 저장된 JWT를 포함하여 서버에 요청&lt;/li&gt;
&lt;li&gt;서버는 전달받은 JWT를 디코딩하여 Header, Payload, Signature 정보를 추출&lt;/li&gt;
&lt;li&gt;서버는 Secret Key를 사용해 Header와 Payload로 새로운 Signature를 생성하고, 이를 JWT의 Signature와 비교합니다. Signature가 일치하고 JWT가 유효한 경우, 클라이언트의 요청에 필요한 응답을 반환&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 애플리케이션에 필요한 정보를 payload에 담아둘 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 로그인과정에서 한번만 DB에 접근하고 그 이후의 인증과정에서는 DB에 접근할 필요가 없음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 서버의 부담을 완화 할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=36lpDzQzVXs&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=36lpDzQzVXs&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Dev/Backend</category>
      <category>jwt</category>
      <author>dong_seok</author>
      <guid isPermaLink="true">https://striver.tistory.com/60</guid>
      <comments>https://striver.tistory.com/entry/Backend-JWT#entry60comment</comments>
      <pubDate>Wed, 11 Dec 2024 14:30:12 +0900</pubDate>
    </item>
  </channel>
</rss>