<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>개발일기장</title>
    <link>https://jjjjqqq.tistory.com/</link>
    <description>공부하는 금융IT 운영자입니다</description>
    <language>ko</language>
    <pubDate>Thu, 7 May 2026 12:14:55 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>NickTop</managingEditor>
    <item>
      <title>OpenAI LLM API: Responses, Chat Completions, Batch</title>
      <link>https://jjjjqqq.tistory.com/109</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;LLM API에는 아직 하나의 공식 표준이 없다. 하지만 OpenAI의 Chat Completions 형식은 너무 널리 쓰이게 되면서 사실상 표준처럼 자리 잡았다. 그래서 대부분의 LLM 서비스는 Chat Completions와 호환되게 LLM Endpoint를 제공한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Chat Completions API&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오랫동안 OpenAI 기반 챗봇 개발의 표준처럼 사용되던 방식.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;messages 배열의 입력을 받고 각 message는 system, user, assistant 같은 role을 가진다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대화를 구성하는 메시지 목록을 주면 모델이 응답을 반환한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SDK 함수는 실제로 /v1/chat/completions를 호출한다&lt;/p&gt;
&lt;pre id=&quot;code_1777125359392&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from openai import OpenAI

client = OpenAI()

completion = client.chat.completions.create(
    model=&quot;gpt-5&quot;,
    messages=[
        {&quot;role&quot;: &quot;system&quot;, &quot;content&quot;: &quot;너는 친절한 고객 응대 챗봇이야&quot;},
        {&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;OpenAI와 Anthropic 모델의 차이가 뭐야?&quot;}
    ]
)

print(completion.choices[0].message.content)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Responses API&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 채팅을 위한 API(chat completions)와 복잡한 기능 수행을 위한 Assistant API가 분리되어있었지만, 에이전트 구축에 용이하도록 Responses API로 병합했다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://openai.com/ko-KR/index/new-tools-for-building-agents/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://openai.com/ko-KR/index/new-tools-for-building-agents/&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;새로운 프로젝트를 구축중이라면 Responses API를 쓰도록 권장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;단, 오디오는 아직 지원하지 않기 때문에 chat completions를 써야한다&lt;/u&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;- Built-in tool : 예) web_search&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Seamless multi-turn&lt;/p&gt;
&lt;pre id=&quot;code_1777126322115&quot; class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;from openai import OpenAI

client = OpenAI()

response = client.responses.create(
    model=&quot;gpt-5&quot;,
    instructions=&quot;너는 친절한 고객 응대 챗봇이야&quot;,
    input=&quot;OpenAI와 Anthropic 모델의 차이가 뭐야?&quot;
)

print(response.output_text)&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;기존 chat completions처럼 배열로 보내도 되지만, previous_response_id를 쓰면 어플리케이션 입장에서 대화이력을 저장소에 담지 않아도 된다&lt;/p&gt;
&lt;pre id=&quot;code_1777126551124&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from openai import OpenAI

client = OpenAI()

response1 = client.responses.create(
    model=&quot;gpt-5&quot;,
    instructions=&quot;너는 친절한 고객 응대 챗봇이야&quot;,
    input=&quot;OpenAI와 Anthropic 모델의 차이가 뭐야?&quot;,
    store=True
)

response2 = client.responses.create(
    model=&quot;gpt-5&quot;,
    instructions=&quot;너는 친절한 고객 응대 챗봇이야&quot;,
    input=&quot;조금 더 자세히 설명해줘&quot;,
    previous_response_id=response1.id,
    store=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;tool 사용시 tools 파라미터에 넣어주면 된다. web_search는 기본제공한다&lt;/p&gt;
&lt;pre id=&quot;code_1777127987299&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;answer = client.responses.create(
    model=&quot;gpt-5.5&quot;,
    input=&quot;Who is the current president of France?&quot;,
    tools=[{&quot;type&quot;: &quot;web_search&quot;}]
)

print(answer.output_text)&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;a href=&quot;https://developers.openai.com/api/docs/guides/migrate-to-responses&quot;&gt;https://developers.openai.com/api/docs/guides/migrate-to-responses&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #181818; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;Batch API&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;&lt;a href=&quot;https://developers.openai.com/api/docs/guides/batch&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developers.openai.com/api/docs/guides/batch&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>개발업무/개발</category>
      <author>NickTop</author>
      <guid isPermaLink="true">https://jjjjqqq.tistory.com/109</guid>
      <comments>https://jjjjqqq.tistory.com/109#entry109comment</comments>
      <pubDate>Sat, 25 Apr 2026 23:47:47 +0900</pubDate>
    </item>
    <item>
      <title>OAuth란</title>
      <link>https://jjjjqqq.tistory.com/108</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;OAuth란?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OAuth 2.0는 제3자 애플리케이션이 HTTP 서비스에 대해 제한된 접근 권한을 얻을 수 있게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(출처 : &lt;span&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc6749&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://datatracker.ietf.org/doc/html/rfc6749&lt;/a&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;유의할 점은, OAuth는 &lt;b&gt;인증이 아니라 권한부여를 토큰으로 표준화한 방식&lt;/b&gt;이다&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;357&quot; data-origin-height=&quot;705&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oJgJx/dJMcaiJmO4P/yEvO3tkrLk1XG9wv4yxD0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oJgJx/dJMcaiJmO4P/yEvO3tkrLk1XG9wv4yxD0k/img.png&quot; data-alt=&quot;LinkedIn OAuth&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oJgJx/dJMcaiJmO4P/yEvO3tkrLk1XG9wv4yxD0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoJgJx%2FdJMcaiJmO4P%2FyEvO3tkrLk1XG9wv4yxD0k%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;357&quot; height=&quot;705&quot; data-origin-width=&quot;357&quot; data-origin-height=&quot;705&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;LinkedIn OAuth&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span&gt;OAuth를 처음 접하면 &quot;구글로 로그인 = OAuth&quot;처럼 보이지만, 정확히 말하면 비&lt;/span&gt;&lt;span&gt;밀번호를 공유하지 않고도, 필요한 권한만 위임해서 API를 안전하게 호출하는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;OAuth가 해결하려는 문제&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linkedin을 구글을 통해 로그인 할때 다음과 같이 할 수 있다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled diagram-2026-03-19-140336.png&quot; data-origin-width=&quot;3250&quot; data-origin-height=&quot;1725&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bk5sFC/dJMcahqcUoU/dXoWEjvgtkvyLy5j495Ft1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bk5sFC/dJMcahqcUoU/dXoWEjvgtkvyLy5j495Ft1/img.png&quot; data-alt=&quot;OAuth 이전 사용자 정보 조회&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bk5sFC/dJMcahqcUoU/dXoWEjvgtkvyLy5j495Ft1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbk5sFC%2FdJMcahqcUoU%2FdXoWEjvgtkvyLy5j495Ft1%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;637&quot; height=&quot;338&quot; data-filename=&quot;Untitled diagram-2026-03-19-140336.png&quot; data-origin-width=&quot;3250&quot; data-origin-height=&quot;1725&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;OAuth 이전 사용자 정보 조회&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span&gt;이 방식은 다음 문제가 크다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;- 제3자(Linkedin)가 비밀번호를 보관하게 됨&lt;/span&gt;&lt;br /&gt;&lt;span&gt;- 최소권한(조회만 허용 등) 통제 어려움&lt;/span&gt;&lt;br /&gt;&lt;span&gt;- 사고 시 피해 범위가 큼&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;OAuth는 이를 토큰(access token)으로 바꿔 해결합니다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;OAuth Role&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;괄호안의 내용은 위 예시와 매핑한것이다&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;156&quot; data-start=&quot;38&quot; data-ke-size=&quot;size16&quot;&gt;resource owner(&lt;b&gt;User&lt;/b&gt;) : 자원(&lt;b&gt;사용자 정보&lt;/b&gt;)의 주인&lt;/p&gt;
&lt;p data-end=&quot;276&quot; data-start=&quot;158&quot; data-ke-size=&quot;size16&quot;&gt;resource server(&lt;b&gt;Google&lt;/b&gt;) : 자원을 제공하는 서버&lt;/p&gt;
&lt;p data-end=&quot;457&quot; data-start=&quot;278&quot; data-ke-size=&quot;size16&quot;&gt;client(&lt;b&gt;Linkedin&lt;/b&gt;) : 자원을 가져오려는 애플리케이션&lt;/p&gt;
&lt;p data-end=&quot;584&quot; data-start=&quot;459&quot; data-ke-size=&quot;size16&quot;&gt;authorization server(&lt;b&gt;Google&lt;/b&gt;) : 클라이언트에게 액세스 토큰을 발급하는 서버. authorization server와 resource server가 다를 수도 있고 같을 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;OAuth 흐름&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled diagram-2026-03-19-142655.png&quot; data-origin-width=&quot;3440&quot; data-origin-height=&quot;1965&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mSEm6/dJMcabp0t3P/6PKu5xNPMKLuLieSvurknk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mSEm6/dJMcabp0t3P/6PKu5xNPMKLuLieSvurknk/img.png&quot; data-alt=&quot;OAuth 기본흐름&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mSEm6/dJMcabp0t3P/6PKu5xNPMKLuLieSvurknk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmSEm6%2FdJMcabp0t3P%2F6PKu5xNPMKLuLieSvurknk%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;643&quot; height=&quot;367&quot; data-filename=&quot;Untitled diagram-2026-03-19-142655.png&quot; data-origin-width=&quot;3440&quot; data-origin-height=&quot;1965&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;OAuth 기본흐름&lt;/figcaption&gt;
&lt;/figure&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;&quot;인증&quot;을 어떻게 하는지는 서비스마다 다르게 구현되어있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 Access Token마다 어떤 resource까지 제공할 수 있는지 제한되어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어, 어떤 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;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;OAuth + OIDC&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;PKCE Authorization Flow-2026-03-21-070320.png&quot; data-origin-width=&quot;5970&quot; data-origin-height=&quot;4850&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAcpY7/dJMcacvHPUN/5wjLJjZjNxP659epwm59W1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAcpY7/dJMcacvHPUN/5wjLJjZjNxP659epwm59W1/img.png&quot; data-alt=&quot;OAuth + OIDC 플로우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAcpY7/dJMcacvHPUN/5wjLJjZjNxP659epwm59W1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAcpY7%2FdJMcacvHPUN%2F5wjLJjZjNxP659epwm59W1%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;5970&quot; height=&quot;4850&quot; data-filename=&quot;PKCE Authorization Flow-2026-03-21-070320.png&quot; data-origin-width=&quot;5970&quot; data-origin-height=&quot;4850&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;OAuth + OIDC 플로우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;962&quot; data-start=&quot;921&quot; data-section-id=&quot;szxgl1&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;OIDC :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;OAuth2.0에서 확장된 ID 인증 프로토콜&lt;/p&gt;
&lt;h3 data-end=&quot;962&quot; data-start=&quot;921&quot; data-section-id=&quot;szxgl1&quot; data-ke-size=&quot;size23&quot;&gt;1) 사용자가 Linkedin에서 &amp;ldquo;Google로 로그인&amp;rdquo;을 누른다&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1083&quot; data-start=&quot;963&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1008&quot; data-start=&quot;963&quot; data-section-id=&quot;geljk9&quot;&gt;이때 &lt;b&gt;Linkedin은 구글 로그인 페이지로 브라우저를 리다이렉트&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1083&quot; data-start=&quot;1009&quot; data-section-id=&quot;1nsfhhp&quot;&gt;중요한 점: 사용자가 구글 ID/PW를 입력하는 곳은 &lt;b&gt;구글 화면&lt;/b&gt;이고, &lt;b&gt;Linkedin은 비밀번호를 절대 받지 않음&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;1138&quot; data-start=&quot;1085&quot; data-section-id=&quot;l7qeho&quot; data-ke-size=&quot;size23&quot;&gt;2) Linkedin &amp;rarr; Google /authorize 요청(브라우저 프론트 채널)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1487&quot; data-start=&quot;1178&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1216&quot; data-start=&quot;1178&quot; data-section-id=&quot;1thhgzi&quot;&gt;client_id: &quot;이 요청은 LinkedIn 앱에서 왔다&quot;&lt;/li&gt;
&lt;li data-end=&quot;1264&quot; data-start=&quot;1217&quot; data-section-id=&quot;5mfpuq&quot;&gt;redirect_uri: &quot;로그인 결과를 LinkedIn의 이 주소로 돌려줘&quot;&lt;/li&gt;
&lt;li data-end=&quot;1325&quot; data-start=&quot;1265&quot; data-section-id=&quot;8nitii&quot;&gt;scope=openid email profile: &quot;로그인(OIDC) + 기본 프로필/이메일을 원해&quot;&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;1745&quot; data-start=&quot;1697&quot; data-section-id=&quot;1d5irov&quot; data-ke-size=&quot;size23&quot;&gt;4) Google &amp;rarr; LinkedIn으로 code 전달(브라우저 리다이렉트)&lt;/h3&gt;
&lt;p data-end=&quot;1783&quot; data-start=&quot;1746&quot; data-ke-size=&quot;size16&quot;&gt;구글은 아래정보와 함께 Linkedin의 redirect_uri로 돌려보냄&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1850&quot; data-start=&quot;1784&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1812&quot; data-start=&quot;1784&quot; data-section-id=&quot;1g5t3d&quot;&gt;code=... (짧은 수명의 1회용 코드)&lt;/li&gt;
&lt;li data-end=&quot;1850&quot; data-start=&quot;1813&quot; data-section-id=&quot;15txh52&quot;&gt;state=... (요청 때 보낸 state와 동일해야 함)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1908&quot; data-start=&quot;1852&quot; data-ke-size=&quot;size16&quot;&gt;Linkedin은 여기서 &lt;b&gt;state가 일치하는지&lt;/b&gt; 확인해서 &quot;중간에 누가 끼어든 요청&quot;을 막음&lt;/p&gt;
&lt;h3 data-end=&quot;1957&quot; data-start=&quot;1910&quot; data-section-id=&quot;1gcrb0y&quot; data-ke-size=&quot;size23&quot;&gt;5) LinkedIn &amp;rarr; Google /token 교환(서버-서버 백채널)&lt;/h3&gt;
&lt;p data-end=&quot;1997&quot; data-start=&quot;1958&quot; data-ke-size=&quot;size16&quot;&gt;이제 Linkedin 서버가 구글에 &lt;b&gt;code를 토큰으로 교환&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;2108&quot; data-start=&quot;2094&quot; data-ke-size=&quot;size16&quot;&gt;구글은 보통 다음을 반환:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2209&quot; data-start=&quot;2109&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2157&quot; data-start=&quot;2109&quot; data-section-id=&quot;a9hdwf&quot;&gt;id_token (JWT): &quot;이 사용자는 누구다&quot;를 &lt;b&gt;서명된 형태로 증명&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;2209&quot; data-start=&quot;2158&quot; data-section-id=&quot;222404&quot;&gt;access_token: 사용자 정보 같은 구글 리소스 API 호출용&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;2257&quot; data-start=&quot;2211&quot; data-section-id=&quot;sn4w8r&quot; data-ke-size=&quot;size23&quot;&gt;6) LinkedIn이 id_token을 검증하고 사용자 계정을 연결한다&lt;/h3&gt;
&lt;p data-end=&quot;2300&quot; data-start=&quot;2258&quot; data-ke-size=&quot;size16&quot;&gt;Linkedin은 id_token을 &lt;b&gt;검증&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2403&quot; data-start=&quot;2301&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2322&quot; data-start=&quot;2301&quot; data-section-id=&quot;upzmqs&quot;&gt;서명 검증(구글이 진짜 발급했는지)&lt;/li&gt;
&lt;li data-end=&quot;2370&quot; data-start=&quot;2323&quot; data-section-id=&quot;vzxgli&quot;&gt;iss(발급자), aud(수신자=Linkedin), exp(만료) 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2415&quot; data-start=&quot;2405&quot; data-ke-size=&quot;size16&quot;&gt;그리고 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;구글 계정의 고유 식별자를 통한 계정 매핑&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-end=&quot;2698&quot; data-start=&quot;2660&quot; data-section-id=&quot;1r6zbv2&quot; data-ke-size=&quot;size23&quot;&gt;8) 최종: LinkedIn이 자체 세션을 만들어 로그인 완료&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2792&quot; data-start=&quot;2699&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2733&quot; data-start=&quot;2699&quot; data-section-id=&quot;xizasn&quot;&gt;LinkedIn은 &amp;ldquo;구글 토큰&amp;rdquo;을 그대로 쓰는 게 아니라,&lt;/li&gt;
&lt;li data-end=&quot;2792&quot; data-start=&quot;2734&quot; data-section-id=&quot;b612qv&quot;&gt;최종적으로 &lt;b&gt;LinkedIn 서비스용 세션(쿠키/세션 토큰)&lt;/b&gt; 을 발급해서 로그인 상태를 유지해.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;br /&gt;&lt;span&gt;3-legged(사용자 대행) vs 2-legged(서비스 대 서비스)&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OAuth에 꼭 end user가 껴있지는 않다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span&gt;3-legged OAuth: 사용자 대행(사용자 동의가 존재)&lt;/span&gt;&lt;br /&gt;&lt;span&gt;Access token의 의미:&lt;/span&gt;&lt;span&gt; 사용자가 A어플리케이션에게 이 scope로 B어플리케이션의 자원 접근을 허락했다.&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;2-legged OAuth: A 시스템이 &quot;자기 자신 자격&quot;으로 토큰을 발급받아 B API를 호출, 이때 사용자가 관여하지 않는다&lt;/p&gt;
&lt;p data-end=&quot;2023&quot; data-start=&quot;1993&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Access Token의 의미 : &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;A 서비스는 이 scope로 B API를 호출할 권한이 있다&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>개발업무/개발</category>
      <author>NickTop</author>
      <guid isPermaLink="true">https://jjjjqqq.tistory.com/108</guid>
      <comments>https://jjjjqqq.tistory.com/108#entry108comment</comments>
      <pubDate>Sat, 21 Mar 2026 16:15:15 +0900</pubDate>
    </item>
    <item>
      <title>LLM이 Temperature=0으로 고정했는데도 결과가 바뀌는 이유</title>
      <link>https://jjjjqqq.tistory.com/107</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;a href=&quot;https://github.com/jhong92-pro/llm-batch-invariance/blob/main/llm-batch-invariance.ipynb&quot;&gt;https://github.com/jhong92-pro/llm-batch-invariance/blob/main/llm-batch-invariance.ipynb&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결합법칙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM의 대부분 연산은 matmul / reduction인데, GPU에서는 병렬 합산이 일어나며 합치는 순서가 바뀌면 반올림 오차가 달라질 수 있다&lt;/p&gt;
&lt;pre id=&quot;code_1769865942627&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import torch
a = torch.Tensor([1e16])
b = torch.Tensor([-1e16])
c = torch.Tensor([1])

print(&quot;(a + b) + c :&quot; , (a + b) + c)
print(&quot;a + (b + c) :&quot; , a + (b + c))&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1769865952893&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(a + b) + c : tensor([1.])
a + (b + c) : tensor([0.])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수학적으로는 둘 다 1이어야 할 것 같지만, floating-point에서는 반올림 때문에 결과가 달라질 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Batch invariance&lt;/h2&gt;
&lt;pre id=&quot;code_1769866030446&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;M, K, N = 512, 512, 512

A0 = torch.randn(M, K, device=device, dtype=dtype)
B0 = torch.randn(K, N, device=device, dtype=dtype)
C_single = A0 @ B0

def make_batched_inputs(batch_size: int):
    A = torch.randn(batch_size, M, K, device=device, dtype=dtype)
    B = torch.randn(batch_size, K, N, device=device, dtype=dtype)
    A[0].copy_(A0)
    B[0].copy_(B0)
    return A, B

batch_sizes = [1,2,3,4,5,6,7,8,9,10,100,1000]

for B in batch_sizes:
    A, Bmat = make_batched_inputs(B)
    C_batch = torch.bmm(A, Bmat)
    diff = C_batch[0] - C_single
    max_abs = diff.abs().max().item()
    l2 = torch.norm(diff).item()
    same = torch.allclose(C_batch[0], C_single, atol=1e-5, rtol=1e-5)
    print(f&quot;Batch {B}: max_abs_diff={max_abs}, l2_diff={l2}, allclose={same}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1769866039299&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Batch 1: max_abs_diff=0.0, l2_diff=0.0, allclose=True
Batch 2: max_abs_diff=0.0001068115234375, l2_diff=0.005174573510885239, allclose=False
Batch 3: max_abs_diff=0.0001068115234375, l2_diff=0.005174573510885239, allclose=False
...
Batch 1000: max_abs_diff=0.0001068115234375, l2_diff=0.005174573510885239, allclose=False&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Batch=1 이후로는 결과가 약간 다르다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커널/연산 전략 차이로 인한 FP 오차차이가 생기기 때문이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;torch.profiler로 operation을 확인하면 아래와 같다&lt;/p&gt;
&lt;pre id=&quot;code_1769866115715&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[single (bmm)] GEMM-related ops: ['aten::bmm', 'ampere_sgemm_32x32_sliced1x4_nn']
[batched (bmm, B=2)] GEMM-related ops: ['aten::bmm', 'ampere_sgemm_128x128_nn']
[batched (bmm, B=200)] GEMM-related ops: ['aten::bmm', 'ampere_sgemm_128x128_nn']&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;간단한 transformer를 만들어서 실험해봐도 layer output이 달라진다(깃허브 코드 참고)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1769866277647&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;B=1: max_abs=4.882812e-03, l2=1.297607e-01 (worst layer_3)
B=4: max_abs=5.859375e-03, l2=1.132202e-01 (worst layer_3)
B=8, 16: exact match (all zeros)
B=32: max_abs=3.906250e-03, l2=7.080078e-02 (worst layer_1)&lt;/code&gt;&lt;/pre&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;h2 data-ke-size=&quot;size26&quot;&gt;GPT-2 테스트&lt;/h2&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 data-end=&quot;4256&quot; data-start=&quot;4237&quot;&gt;모델: gpt2-medium&lt;/li&gt;
&lt;li data-end=&quot;4287&quot; data-start=&quot;4257&quot;&gt;decoding: greedy(argmax)&lt;/li&gt;
&lt;li data-end=&quot;4307&quot; data-start=&quot;4288&quot;&gt;baseline: batch=2&lt;/li&gt;
&lt;li data-end=&quot;4333&quot; data-start=&quot;4308&quot;&gt;비교: batch=3,4,7,8,16,32&lt;/li&gt;
&lt;li data-end=&quot;4385&quot; data-start=&quot;4334&quot;&gt;배치 구성: 0번은 동일 prompt, 나머지는 distractor(다른 질문들)&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_1769866352983&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def build_batch(batch_size: int, prompt: str):
    &quot;&quot;&quot;index0 : prompt / rest : DISTRACTORS&quot;&quot;&quot;
    if batch_size == 1:
        return [prompt]
    need = batch_size - 1
    return [prompt] + DISTRACTORS[:need]&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;Tell me about yourself.\nanswer : &quot; input에서 바로 flip이 관측된다&lt;/p&gt;
&lt;pre id=&quot;code_1769866386556&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;=== prompt 1/103, Flip checks (vs baseline) ===
Baseline(B=2) : I'm a professional artist. I'm a professional ...
batch=3: FLIPS at steps: [ 4  5  6  7  8  9 10 11 12 13 14 15 16 17 18]...
batch=4: FLIPS at steps: [ 4  5  6  7  8  9 10 11 12 13 14 15 16 17 18]...
batch=7: FLIPS at steps: [ 4  5  6  7  8  9 10 11 12 13 14 15 16 17 18]...
...

[Baseline B=2 top-k]
  '\xa0': -90.499222
  ' young': -90.501602
  ' writer': -90.504623
  ' woman': -90.577217
  ' student': -90.678535
  ' man': -90.699829
  ' guy': -90.841461
  ' professional': -90.907951
  ' twenty': -91.086029
  ' 22': -91.121254
[Batch B=3 top-k]
  ' young': -90.602028
  ' writer': -90.617401
  '\xa0': -90.664429
  ' woman': -90.695312
  ' student': -90.778191
  ' man': -90.795197
  ' guy': -90.942856
  ' professional': -91.005234
  ' twenty': -91.189301
  ' 22': -91.211235
  ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분류 모델도 애매한 샘플에서는 top-1과 top-2의 차이가 작아질 수 있지만, 보통 결정은 한 번만 내리고 그 결과가 다음 입력으로 피드백되지 않는다. 반면 LLM은 토큰을 여러 번 연속으로 선택해야 하고, next-token 분포에서 top-1/top-2 마진이 작은 순간이 자주 나타난다. 그래서 아주 작은 수치 차이(배치/커널/반올림)가 특정 스텝에서 top-1을 바꾸면 토큰 flip이 발생하고, 그 flip이 이후 생성 과정 전체로 누적 및 증폭되기 쉽다.&lt;/p&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;h2 data-ke-size=&quot;size26&quot;&gt;실무적으로 왜 중요할까?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요청 단위로 caching할 때, 항상 같은 답을 기대하면 깨질 수 있음&lt;/li&gt;
&lt;li data-end=&quot;5981&quot; data-start=&quot;5955&quot;&gt;golden set 평가를 배치로 돌릴 때, 배치 크기나 배치 구성(정렬/섞임)에 따라 결과가 미세하게 달라짐&lt;/li&gt;
&lt;li data-end=&quot;5981&quot; data-start=&quot;5955&quot;&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;</description>
      <category>인공지능</category>
      <author>NickTop</author>
      <guid isPermaLink="true">https://jjjjqqq.tistory.com/107</guid>
      <comments>https://jjjjqqq.tistory.com/107#entry107comment</comments>
      <pubDate>Sat, 31 Jan 2026 22:41:44 +0900</pubDate>
    </item>
    <item>
      <title>사내 교육용 자연어처리 ppt</title>
      <link>https://jjjjqqq.tistory.com/106</link>
      <description>&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/cK8QBJ/dJMb9aqatMd/eaXVu3lj8A2xv2EknUlqvk/%EC%9E%90%EC%97%B0%EC%96%B4%EC%B2%98%EB%A6%AC%EA%B8%B0%EC%B4%88%EB%B0%9C%ED%91%9C%EC%9E%90%EB%A3%8C.pptx?attach=1&amp;amp;knm=tfile.pptx&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;자연어처리기초발표자료.pptx&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.64MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&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;수학적인 내용은 없애고 개념 및 용어들에 친숙해질수있도록 내용구성했습니다&lt;/p&gt;</description>
      <category>인공지능</category>
      <author>NickTop</author>
      <guid isPermaLink="true">https://jjjjqqq.tistory.com/106</guid>
      <comments>https://jjjjqqq.tistory.com/106#entry106comment</comments>
      <pubDate>Tue, 21 Oct 2025 23:50:18 +0900</pubDate>
    </item>
    <item>
      <title>Spring Boot: WebClient vs RestTemplate</title>
      <link>https://jjjjqqq.tistory.com/105</link>
      <description>&lt;p data-end=&quot;293&quot; data-start=&quot;201&quot; data-ke-size=&quot;size16&quot;&gt;Spring&amp;nbsp;Boot에서&amp;nbsp;외부&amp;nbsp;API를&amp;nbsp;호출할&amp;nbsp;때&amp;nbsp;가장&amp;nbsp;많이&amp;nbsp;쓰이는&amp;nbsp;두&amp;nbsp;가지가&amp;nbsp;있습니다. &lt;br /&gt;RestTemplate과 WebClient입니다. &lt;br /&gt;&lt;br /&gt;RestTemplate : 오래된 방식 (동기/블로킹) &lt;br /&gt;WebClient : 새로운 방식 (Spring 5에서 새로 추가, 비동기/논블로킹, Spring WebFlux 기반)&lt;/p&gt;
&lt;p data-end=&quot;293&quot; data-start=&quot;201&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;293&quot; data-start=&quot;201&quot; data-ke-size=&quot;size26&quot;&gt;RestTemplate&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;544&quot; data-start=&quot;455&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;486&quot; data-start=&quot;455&quot;&gt;Spring 3부터 제공된 오래된 HTTP 클라이언트&lt;/li&gt;
&lt;li data-end=&quot;529&quot; data-start=&quot;487&quot;&gt;동기(Blocking) 방식 : 요청이 끝날 때까지 스레드가 대기&lt;/li&gt;
&lt;li data-end=&quot;529&quot; data-start=&quot;487&quot;&gt;요청을 보낼 때마다 스레드를 점유 (1000개의 요청을 한번에 받으려면 1000개의 스레드 필요)&lt;/li&gt;
&lt;li data-end=&quot;529&quot; data-start=&quot;487&quot;&gt;트래픽이 많으면 스레드 풀을 크게 잡아야함&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1759236287044&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	public static void main(String[] args) {
		RestTemplate restTemplate = new RestTemplate();
		String url = &quot;https://www.naver.com&quot;;

		ResponseEntity&amp;lt;String&amp;gt; response =
				restTemplate.getForEntity(url, String.class);

		System.out.println(response.getBody());
	}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1106&quot; data-start=&quot;1091&quot; data-ke-size=&quot;size26&quot;&gt;WebClient&lt;/h2&gt;
&lt;h3 data-end=&quot;1114&quot; data-start=&quot;1108&quot; data-ke-size=&quot;size23&quot;&gt;특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1246&quot; data-start=&quot;1115&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1155&quot; data-start=&quot;1115&quot;&gt;Spring 5 (WebFlux)에서 새로 추가된 HTTP 클라이언트&lt;/li&gt;
&lt;li data-end=&quot;1187&quot; data-start=&quot;1156&quot;&gt;비동기(Non-blocking) 방식 지원&lt;/li&gt;
&lt;li data-end=&quot;1230&quot; data-start=&quot;1188&quot;&gt;Reactor 기반 - Mono/Flux 리액티브 타입으로 결과 반환 (아직 도착하지 않은 값 표현, 코드 작성이 상대적으로 어려움)&lt;/li&gt;
&lt;li data-end=&quot;1246&quot; data-start=&quot;1231&quot;&gt;동기/비동기 둘 다 가능&lt;/li&gt;
&lt;li data-end=&quot;1246&quot; data-start=&quot;1231&quot;&gt;이벤트루프 기반 : 하나의 스레드가 여러 작업 상태 변화를 감시하고 처리가능한 작업을 전달, 즉 하나의 스레드로 여러 작업 처리 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1759236330623&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    public static void main(String[] args) {
        WebClient webClient = WebClient.create(&quot;https://www.naver.com&quot;);

        Mono&amp;lt;String&amp;gt; response = webClient.get()
                .retrieve()
                .bodyToMono(String.class);

        // 비동기 실행 &amp;rarr; subscribe
        response.subscribe(System.out::println);

        // 동기로 결과 받기
        String result = response.block();
        System.out.println(&quot;동기 결과: &quot; + result);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RestTemplate은 아직도 사용중이지만, 성능은 webclient가 더 좋기 때문에 spring에서는 Webclient를 쓰는것을 권유하고 있습니다&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #191e1e; text-align: start;&quot;&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;background-color: #ebf2f2;&quot;&gt;RestTemplate&lt;span&gt;&amp;nbsp;&lt;/span&gt;is in maintenance mode, with only requests for minor changes and bugs to be accepted. Please, consider using the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #1565c0;&quot; href=&quot;https://docs.spring.io/spring-framework/reference/6.0/web/webflux-webclient.html&quot;&gt;WebClient&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;instead.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/reference/6.0/integration/rest-clients.html?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.spring.io/spring-framework/reference/6.0/integration/rest-clients.html&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>개발업무/개발</category>
      <author>NickTop</author>
      <guid isPermaLink="true">https://jjjjqqq.tistory.com/105</guid>
      <comments>https://jjjjqqq.tistory.com/105#entry105comment</comments>
      <pubDate>Tue, 30 Sep 2025 21:55:28 +0900</pubDate>
    </item>
    <item>
      <title>LoadRunner로 WebSocket(STOMP) 부하 테스트하기</title>
      <link>https://jjjjqqq.tistory.com/104</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;로드러너&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LoadRunner는 기업에서 가장 널리 쓰이는 성능&amp;middot;부하 테스트 도구 중 하나입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;697&quot; data-start=&quot;332&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;446&quot; data-start=&quot;332&quot;&gt;VuGen (Virtual User Generator)
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;446&quot; data-start=&quot;375&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;400&quot; data-start=&quot;375&quot;&gt;가상의 사용자 스크립트 작성&amp;middot;녹화 도구&lt;/li&gt;
&lt;li data-end=&quot;446&quot; data-start=&quot;404&quot;&gt;웹, 모바일, DB, 메시징, WebSocket 등 다양한 프로토콜 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;525&quot; data-start=&quot;447&quot;&gt;Controller
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;525&quot; data-start=&quot;470&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;486&quot; data-start=&quot;470&quot;&gt;시나리오 설계 및 실행&lt;/li&gt;
&lt;li data-end=&quot;525&quot; data-start=&quot;490&quot;&gt;동시 사용자 수, 부하 패턴(증가&amp;middot;감소), 런타임 설정 제어&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;622&quot; data-start=&quot;526&quot;&gt;Load Generators
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;622&quot; data-start=&quot;554&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;580&quot; data-start=&quot;554&quot;&gt;실제로 Vuser 부하를 발생시키는 엔진&lt;/li&gt;
&lt;li data-end=&quot;622&quot; data-start=&quot;584&quot;&gt;분산 환경에서 여러 대를 동시에 동작시켜 대규모 트래픽 발생 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;697&quot; data-start=&quot;623&quot;&gt;Analysis
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;697&quot; data-start=&quot;644&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;667&quot; data-start=&quot;644&quot;&gt;실행 결과를 그래프&amp;middot;리포트로 시각화&lt;/li&gt;
&lt;li data-end=&quot;697&quot; data-start=&quot;671&quot;&gt;응답 시간, TPS, 자원 사용량 비교&amp;middot;분석&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;테스트 대상 어플리케이션&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 환경에서 테스트를 진행합시다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[브라우저]&amp;lt;--SockJS--&amp;gt;[서버(WsEndpoint)]&amp;rarr;STOMP&amp;nbsp;프로토콜&amp;rarr;[메시지&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;아래 github 예시가 있어서 가져왔습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/zacscoding/spring-websocket-random-chat/tree/master&quot;&gt;https://github.com/zacscoding/spring-websocket-random-chat/tree/master&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;아래와 같은 흐름입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Join버튼으로 접속&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 다른 사용자가 Join을 할때까지 기다림&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 2명의 사용자가 Join버튼을 눌렸다면 서버가 두 사용자에게 chatRoomId 발급&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. chatRoomId로 웹소켓 연결&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 채팅시작&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;user를 변수로 설정할 수 없어서 코드를 약간 수정했습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/jhong92-pro/spring-websocket-random-chat&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/jhong92-pro/spring-websocket-random-chat&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;VuGen&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글은 OpenText_Performance_Engineering_25.3_Community_Edition로 작성했습니다 (회사에서 쓰는 버전이랑은 다름)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음켜면 New Script and Solution으로 스크립트를 생성합니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;605&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l0lNF/btsP9NWeVxV/NV0wmP3YMaOTTCMLDGKev1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l0lNF/btsP9NWeVxV/NV0wmP3YMaOTTCMLDGKev1/img.png&quot; data-alt=&quot;첫 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l0lNF/btsP9NWeVxV/NV0wmP3YMaOTTCMLDGKev1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl0lNF%2FbtsP9NWeVxV%2FNV0wmP3YMaOTTCMLDGKev1%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;486&quot; height=&quot;374&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;605&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;화면 상단 Record &amp;gt; Record를 클릭합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저가 실행되는데, 여기서 실행되는 모든 이벤트가 코드로 생성됩니다&lt;/p&gt;
&lt;p style=&quot;position: absolute;&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;218&quot; data-origin-height=&quot;173&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cU6k1j/btsQa3lNx0Z/5D5h8e5kUvPCzhqPS1gam1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cU6k1j/btsQa3lNx0Z/5D5h8e5kUvPCzhqPS1gam1/img.png&quot; data-alt=&quot;Record&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cU6k1j/btsQa3lNx0Z/5D5h8e5kUvPCzhqPS1gam1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcU6k1j%2FbtsQa3lNx0Z%2F5D5h8e5kUvPCzhqPS1gam1%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;362&quot; height=&quot;287&quot; data-origin-width=&quot;218&quot; data-origin-height=&quot;173&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Record&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;588&quot; data-origin-height=&quot;608&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJbSZ5/btsQfbXgN6j/dYJtNW5nAPUsumgPWzzybk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJbSZ5/btsQfbXgN6j/dYJtNW5nAPUsumgPWzzybk/img.png&quot; data-alt=&quot;recording 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJbSZ5/btsQfbXgN6j/dYJtNW5nAPUsumgPWzzybk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJbSZ5%2FbtsQfbXgN6j%2FdYJtNW5nAPUsumgPWzzybk%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;458&quot; height=&quot;474&quot; data-origin-width=&quot;588&quot; data-origin-height=&quot;608&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;recording 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 Action.h에 스크립트를 생성하겠습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vuser_init은 vuser가 처음 생성될때 실행되는 스크립트이고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;action은 부하테스트할 transaction을 명시하는 부분입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vuser가 없어질때 vuser_end가 실행됩니다&lt;/p&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 address에 localhost:8080/#user4를 입력하면 해당 페이지로 넘어갔던것같은데 안되네요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;채팅 연결을 해주어야하기때문에 다른 브라우저 localhost:8080/#user3 열어서 join까지 눌려줬습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 record되는 브라우저에서는 join눌리고 Hello까지 send합니다&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;1149&quot; data-origin-height=&quot;852&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tcUej/btsQfyq8Wa4/JTTRE29g3oEIBp2bFicso0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tcUej/btsQfyq8Wa4/JTTRE29g3oEIBp2bFicso0/img.png&quot; data-alt=&quot;vugen 화면 녹화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tcUej/btsQfyq8Wa4/JTTRE29g3oEIBp2bFicso0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtcUej%2FbtsQfyq8Wa4%2FJTTRE29g3oEIBp2bFicso0%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;1149&quot; height=&quot;852&quot; data-origin-width=&quot;1149&quot; data-origin-height=&quot;852&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;vugen 화면 녹화&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 완료하면 Action.h에 스크립트가 생깁니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(브라우저에 따라 스크립트가 다르게 생성될 수 있습니다)&lt;/p&gt;
&lt;pre id=&quot;code_1756466480331&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Action()
{

	web_set_sockets_option(&quot;SSL_VERSION&quot;, &quot;AUTO&quot;);

	web_url(&quot;seed&quot;, 
		&quot;URL=https://clientservices.googleapis.com/chrome-variations/seed?osname=win&amp;amp;channel=stable&amp;amp;milestone=139&quot;, 
		&quot;Resource=0&quot;, 
		&quot;Referer=&quot;, 
		&quot;Snapshot=t17.inf&quot;, 
		&quot;Mode=HTML&quot;, 
		LAST);

	lr_think_time(5);

	web_url(&quot;localhost:8080&quot;, 
		&quot;URL=http://localhost:8080/&quot;, 
		&quot;Resource=0&quot;, 
		&quot;RecContentType=text/html&quot;, 
		&quot;Referer=&quot;, 
		&quot;Snapshot=t18.inf&quot;, 
		&quot;Mode=HTML&quot;, 
		EXTRARES, 
		&quot;Url=/favicon.ico&quot;, ENDITEM, 
		&quot;Url=https://content-autofill.googleapis.com/v1/pages/ChVDaHJvbWUvMTM5LjAuNzI1OC4xMzkSGQm5Nc-FPgmrixIFDbXFZk8hIWaXE1v3UzI=?alt=proto&quot;, &quot;Referer=&quot;, ENDITEM, 
		LAST);

	web_custom_request(&quot;user1&quot;, 
		&quot;URL=http://localhost:8080/join/user4&quot;, 
		&quot;Method=GET&quot;, 
		&quot;Resource=0&quot;, 
		&quot;RecContentType=application/json&quot;, 
		&quot;Referer=http://localhost:8080/&quot;, 
		&quot;Snapshot=t19.inf&quot;, 
		&quot;Mode=HTML&quot;, 
		LAST);

	web_url(&quot;info&quot;, 
		&quot;URL=http://localhost:8080/chat-websocket/info?t=1756466220884&quot;, 
		&quot;Resource=0&quot;, 
		&quot;RecContentType=application/json&quot;, 
		&quot;Referer=http://localhost:8080/&quot;, 
		&quot;Snapshot=t20.inf&quot;, 
		&quot;Mode=HTML&quot;, 
		EXTRARES, 
		&quot;Url=https://content-autofill.googleapis.com/v1/pages/ChVDaHJvbWUvMTM5LjAuNzI1OC4xMzkSIAkf41AQ3bHCORIFDbXFZk8SBQ0ZM-ltIQAtAEQDk8TN?alt=proto&quot;, &quot;Referer=&quot;, ENDITEM, 
		LAST);

	web_custom_request(&quot;xhr_streaming&quot;, 
		&quot;URL=http://localhost:8080/chat-websocket/264/3aheysmp/xhr_streaming?t=1756466221550&quot;, 
		&quot;Method=POST&quot;, 
		&quot;Resource=0&quot;, 
		&quot;RecContentType=application/javascript&quot;, 
		&quot;Referer=http://localhost:8080/&quot;, 
		&quot;Snapshot=t21.inf&quot;, 
		&quot;Mode=HTML&quot;, 
		&quot;EncType=&quot;, 
		LAST);

	web_websocket_connect(&quot;ID=0&quot;, 
		&quot;URI=ws://localhost:8080/chat-websocket/264/20xkw5rr/websocket&quot;, 
		&quot;Origin=http://localhost:8080&quot;, 
		&quot;SecWebSocketExtensions=permessage-deflate; client_max_window_bits&quot;, 
		&quot;OnOpenCB=OnOpenCB0&quot;, 
		&quot;OnMessageCB=OnMessageCB0&quot;, 
		&quot;OnErrorCB=OnErrorCB0&quot;, 
		&quot;OnCloseCB=OnCloseCB0&quot;, 
		LAST);

	
	/*Connection ID 0 received buffer WebSocketReceive0*/

	web_websocket_send(&quot;ID=0&quot;, 
       &quot;Buffer=[\&quot;CONNECT\\nuserId:user4\\nchatRoomId:4ba9f87a-c749-436d-90e0-899091e6a432\\naccept-version:1.1,1.0\\nheart-beat:10000,10000\\n\\n\\u0000\&quot;]&quot;,
		&quot;IsBinary=0&quot;, 
		LAST);

	/*Connection ID 0 received buffer WebSocketReceive1*/

	web_websocket_send(&quot;ID=0&quot;, 
       &quot;Buffer=[\&quot;SUBSCRIBE\\nid:sub-0\\ndestination:/topic/chat/4ba9f87a-c749-436d-90e0-899091e6a432\\n\\n\\u0000\&quot;]&quot;,
		&quot;IsBinary=0&quot;, 
		LAST);
	
	lr_think_time(5);

	web_websocket_send(&quot;ID=0&quot;, 
       &quot;Buffer=[\&quot;SEND\\ndestination:/app/chat.message/4ba9f87a-c749-436d-90e0-899091e6a432\\ncontent-length:66\\n\\n{\\\&quot;messageType\\\&quot;:\\\&quot;CHAT\\\&quot;,\\\&quot;senderSessionId\\\&quot;:\\\&quot;user4\\\&quot;,\\\&quot;message\\\&quot;:\\\&quot;Hello\\\&quot;}\\u0000\&quot;]&quot;,
		&quot;IsBinary=0&quot;, 
		LAST);

	/*Connection ID 0 received buffer WebSocketReceive2*/

	return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에 있는 버전은 web_websocket_send를 자동으로 만들지 못했었는데 이러한 경우, 브라우저 네트워크 탭을 복사하여 코드를 넣으면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저 탭에 있는 웹소켓 자체가 Array에 감싸진 String이라 그대로 복사 붙여넣기 하면 오류가 나고 []를 넣고 &quot;와 \는 escape하여 넣어야합니다.&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째로 chatRoomId가 상수가 아닌 API respone를 통해 변수로 받아야합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #e65700;&quot;&gt;web_reg_save_param&lt;/span&gt; 를 통해서 변수를 받을 수 있습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;left와 right boundary를 지정하고, 이후 response중에서 left와 right boundary가 있으면 감지하여 변수로 저장합니다&lt;/p&gt;
&lt;pre id=&quot;code_1756466828701&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	web_reg_save_param(&quot;chatRoomId&quot;,
	    &quot;LB=\&quot;chatRoomId\&quot;:\&quot;&quot;,    // Left boundary
	    &quot;RB=\&quot;&quot;,                  // Right boundary
	    LAST);

	web_custom_request(&quot;user8&quot;, 
		&quot;URL=http://localhost:8080/join/user8&quot;, 
		&quot;Method=GET&quot;, 
		&quot;Resource=0&quot;, 
		&quot;RecContentType=application/json&quot;, 
		&quot;Referer=http://localhost:8080/&quot;, 
		&quot;Snapshot=t12.inf&quot;, 
		&quot;Mode=HTML&quot;, 
		LAST);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 &quot;chatRootId&quot;: 와 &quot;사이에 있는 값을 chatRoomId라는 변수로 저장합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 저장한 변수는 lr_eval_string으로 값을 가져올 수 있습니다&lt;/p&gt;
&lt;pre id=&quot;code_1756466987702&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	web_websocket_send(&quot;ID=0&quot;, 
       lr_eval_string(&quot;Buffer=[\&quot;CONNECT\\nuserId:user8\\nchatRoomId:{chatRoomId}\\naccept-version:1.1,1.0\\nheart-beat:10000,10000\\n\\n\\u0000\&quot;]&quot;),
		&quot;IsBinary=0&quot;, 
		LAST);

	/*Connection ID 0 received buffer WebSocketReceive1*/

	web_websocket_send(&quot;ID=0&quot;, 
       lr_eval_string(&quot;Buffer=[\&quot;SUBSCRIBE\\nid:sub-0\\ndestination:/topic/chat/{chatRoomId}\\n\\n\\u0000\&quot;]&quot;),
		&quot;IsBinary=0&quot;, 
		LAST);


	web_websocket_send(&quot;ID=0&quot;, 
       lr_eval_string(&quot;Buffer=[\&quot;SEND\\ndestination:/app/chat.message/{chatRoomId}\\ncontent-length:66\\n\\n{\\\&quot;messageType\\\&quot;:\\\&quot;CHAT\\\&quot;,\\\&quot;senderSessionId\\\&quot;:\\\&quot;user8\\\&quot;,\\\&quot;message\\\&quot;:\\\&quot;Hello\\\&quot;}\\u0000\&quot;]&quot;),
		&quot;IsBinary=0&quot;, 
		LAST);&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;두번째는 여러명의 유저로 부하테스트를 하려면 user도 파라미터 처리해야합니다 (나중에 Controller에서 써야함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면 좌측 Parameters를 더블클릭합니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/295Ft/btsQewUR70Y/lepKbPA1aOSkuPTlhGOLv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/295Ft/btsQewUR70Y/lepKbPA1aOSkuPTlhGOLv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/295Ft/btsQewUR70Y/lepKbPA1aOSkuPTlhGOLv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F295Ft%2FbtsQewUR70Y%2FlepKbPA1aOSkuPTlhGOLv1%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;674&quot; height=&quot;648&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;648&quot;/&gt;&lt;/span&gt;&lt;/figure&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;저는 Vuser마다 서로 다른 user 하나만 지정되게 하고 싶어서 select next row와 update value on을 위와 같이 지정했습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;user 파라미터를 불러옵니다 (web_custom_request에는 lr_eval_string 없어도 {}안에 값 넣으면 알아서 세팅됩니다)&lt;/p&gt;
&lt;pre id=&quot;code_1756467393737&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	web_custom_request(&quot;user8&quot;, 
		&quot;URL=http://localhost:8080/join/{user}&quot;, 
		&quot;Method=GET&quot;, 
		&quot;Resource=0&quot;, 
		&quot;RecContentType=application/json&quot;, 
		&quot;Referer=http://localhost:8080/&quot;, 
		&quot;Snapshot=t12.inf&quot;, 
		&quot;Mode=HTML&quot;, 
		LAST);
        
        
        ...
        
        
   	web_websocket_send(&quot;ID=0&quot;, 
       lr_eval_string(&quot;Buffer=[\&quot;CONNECT\\nuserId:{user}\\nchatRoomId:{chatRoomId}\\naccept-version:1.1,1.0\\nheart-beat:10000,10000\\n\\n\\u0000\&quot;]&quot;),
		&quot;IsBinary=0&quot;, 
		LAST);

	/*Connection ID 0 received buffer WebSocketReceive1*/

	web_websocket_send(&quot;ID=0&quot;, 
       lr_eval_string(&quot;Buffer=[\&quot;SUBSCRIBE\\nid:sub-0\\ndestination:/topic/chat/{chatRoomId}\\n\\n\\u0000\&quot;]&quot;),
		&quot;IsBinary=0&quot;, 
		LAST);


	web_websocket_send(&quot;ID=0&quot;, 
       lr_eval_string(&quot;Buffer=[\&quot;SEND\\ndestination:/app/chat.message/{chatRoomId}\\ncontent-length:66\\n\\n{\\\&quot;messageType\\\&quot;:\\\&quot;CHAT\\\&quot;,\\\&quot;senderSessionId\\\&quot;:\\\&quot;{user}\\\&quot;,\\\&quot;message\\\&quot;:\\\&quot;Hello\\\&quot;}\\u0000\&quot;]&quot;),
		&quot;IsBinary=0&quot;, 
		LAST);&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;세번째로는 sockJS가 첫 연결할때 (serverId)/(sessionId)형태로 지정하므로 sessionId만 난수로 바꿔줍니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ws://localhost:8080/chat-websocket/264/20xkw5rr/websocket&lt;/p&gt;
&lt;pre id=&quot;code_1756471139778&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;time.h&quot;

Action()
{
    char buf[8];
    int i;
    const char charset[] = &quot;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789&quot;;

    srand(time(NULL) + lr_eval_string(&quot;{user}&quot;));

    for (i = 0; i &amp;lt; 8; i++) {
        int key = rand() % (int)(sizeof(charset) - 1);
        buf[i] = charset[key];
    }

    lr_output_message(&quot;Random string = %s&quot;, buf);

    lr_save_string(buf, &quot;randStr&quot;);

...


	web_websocket_connect(&quot;ID=0&quot;, 
      lr_eval_string(&quot;URI=ws://localhost:8080/chat-websocket/264/{randStr}/websocket&quot;),
		&quot;Origin=http://localhost:8080&quot;, 
		&quot;SecWebSocketExtensions=permessage-deflate; client_max_window_bits&quot;, 
		&quot;OnOpenCB=OnOpenCB0&quot;, 
		&quot;OnMessageCB=OnMessageCB0&quot;, 
		&quot;OnErrorCB=OnErrorCB0&quot;, 
		&quot;OnCloseCB=OnCloseCB0&quot;, 
		LAST);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;char나 int 같은 변수선언은 함수 최상단에 선언해야하며, 중간에 선언할 경우 오류가 발생합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 content-length는 STOMP 프로토콜에서 바디-길이와 실제 메시지 길이 불일치가 발생할 수 있으므로 없앱니다&lt;/p&gt;
&lt;pre id=&quot;code_1756471185992&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	web_websocket_send(&quot;ID=0&quot;, 
       lr_eval_string(&quot;Buffer=[\&quot;SEND\\ndestination:/app/chat.message/{chatRoomId}\\n\\n{\\\&quot;messageType\\\&quot;:\\\&quot;CHAT\\\&quot;,\\\&quot;senderSessionId\\\&quot;:\\\&quot;{user}\\\&quot;,\\\&quot;message\\\&quot;:\\\&quot;Hello\\\&quot;}\\u0000\&quot;]&quot;),
		&quot;IsBinary=0&quot;, 
		LAST);&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;맨마지막 웹소켓 통신 부분만 Action.h에 두고 나머지는 vuser_init으로 옮긴 최종 코드입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vuser_init.c&lt;/p&gt;
&lt;pre id=&quot;code_1756471232165&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* -------------------------------------------------------------------------------
	Script Title       : 
	Script Description : 
                        
                        
	Recorder Version   : 0
   ------------------------------------------------------------------------------- */
#include &quot;time.h&quot;

vuser_init()
{
    char buf[8];
    int i;
    const char charset[] = &quot;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789&quot;;

    srand(time(NULL) + lr_eval_string(&quot;{user}&quot;));

    for (i = 0; i &amp;lt; 8; i++) {
        int key = rand() % (int)(sizeof(charset) - 1);
        buf[i] = charset[key];
    }

    lr_output_message(&quot;Random string = %s&quot;, buf);

    lr_save_string(buf, &quot;randStr&quot;);

	web_url(&quot;localhost:8080&quot;, 
		&quot;URL=http://localhost:8080/&quot;, 
		&quot;Resource=0&quot;, 
		&quot;RecContentType=text/html&quot;, 
		&quot;Referer=&quot;, 
		&quot;Snapshot=t11.inf&quot;, 
		&quot;Mode=HTML&quot;, 
		EXTRARES, 
		&quot;Url=/favicon.ico&quot;, ENDITEM, 
		&quot;Url=https://content-autofill.googleapis.com/v1/pages/ChVDaHJvbWUvMTM5LjAuNzI1OC4xMzkSGQm5Nc-FPgmrixIFDbXFZk8hIWaXE1v3UzI=?alt=proto&quot;, &quot;Referer=&quot;, ENDITEM, 
		LAST);
	
	web_reg_save_param(&quot;chatRoomId&quot;,
	    &quot;LB=\&quot;chatRoomId\&quot;:\&quot;&quot;,    // Left boundary
	    &quot;RB=\&quot;&quot;,                  // Right boundary
	    LAST);

	web_custom_request(&quot;user8&quot;, 
		&quot;URL=http://localhost:8080/join/{user}&quot;, 
		&quot;Method=GET&quot;, 
		&quot;Resource=0&quot;, 
		&quot;RecContentType=application/json&quot;, 
		&quot;Referer=http://localhost:8080/&quot;, 
		&quot;Snapshot=t12.inf&quot;, 
		&quot;Mode=HTML&quot;, 
		LAST);
	
	lr_output_message(lr_eval_string(&quot;{chatRoomId}&quot;));

	web_url(&quot;info&quot;, 
		&quot;URL=http://localhost:8080/chat-websocket/info?t=1756299447468&quot;, 
		&quot;Resource=0&quot;, 
		&quot;RecContentType=application/json&quot;, 
		&quot;Referer=http://localhost:8080/&quot;, 
		&quot;Snapshot=t13.inf&quot;, 
		&quot;Mode=HTML&quot;, 
		EXTRARES, 
		&quot;Url=https://content-autofill.googleapis.com/v1/pages/ChVDaHJvbWUvMTM5LjAuNzI1OC4xMzkSIAkf41AQ3bHCORIFDbXFZk8SBQ0ZM-ltIQAtAEQDk8TN?alt=proto&quot;, &quot;Referer=&quot;, ENDITEM, 
		LAST);

	web_websocket_connect(&quot;ID=0&quot;, 
      lr_eval_string(&quot;URI=ws://localhost:8080/chat-websocket/264/{randStr}/websocket&quot;),
		&quot;Origin=http://localhost:8080&quot;, 
		&quot;SecWebSocketExtensions=permessage-deflate; client_max_window_bits&quot;, 
		&quot;OnOpenCB=OnOpenCB0&quot;, 
		&quot;OnMessageCB=OnMessageCB0&quot;, 
		&quot;OnErrorCB=OnErrorCB0&quot;, 
		&quot;OnCloseCB=OnCloseCB0&quot;, 
		LAST);

	/*Connection ID 0 received buffer WebSocketReceive0*/

	web_websocket_send(&quot;ID=0&quot;, 
       lr_eval_string(&quot;Buffer=[\&quot;CONNECT\\nuserId:{user}\\nchatRoomId:{chatRoomId}\\naccept-version:1.1,1.0\\nheart-beat:10000,10000\\n\\n\\u0000\&quot;]&quot;),
		&quot;IsBinary=0&quot;, 
		LAST);

	/*Connection ID 0 received buffer WebSocketReceive1*/

	web_websocket_send(&quot;ID=0&quot;, 
       lr_eval_string(&quot;Buffer=[\&quot;SUBSCRIBE\\nid:sub-0\\ndestination:/topic/chat/{chatRoomId}\\n\\n\\u0000\&quot;]&quot;),
		&quot;IsBinary=0&quot;, 
		LAST);



	return 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;Action.h&lt;/p&gt;
&lt;pre id=&quot;code_1756471207027&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Action()
{
	web_websocket_send(&quot;ID=0&quot;, 
       lr_eval_string(&quot;Buffer=[\&quot;SEND\\ndestination:/app/chat.message/{chatRoomId}\\n\\n{\\\&quot;messageType\\\&quot;:\\\&quot;CHAT\\\&quot;,\\\&quot;senderSessionId\\\&quot;:\\\&quot;{user}\\\&quot;,\\\&quot;message\\\&quot;:\\\&quot;Hello\\\&quot;}\\u0000\&quot;]&quot;),
		&quot;IsBinary=0&quot;, 
		LAST);

	return 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;또한, vuser_init에서 연결한 websocket은 action.h에서 연결이 끊기기 때문에 Runtime Settings &amp;gt; Browser Emulation &amp;gt; Simulate a new user on each iteration 체크해제합니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1006&quot; data-origin-height=&quot;505&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biWHEZ/btsQffSVIc3/wkD1weN2XCoq1kvmENpUoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biWHEZ/btsQffSVIc3/wkD1weN2XCoq1kvmENpUoK/img.png&quot; data-alt=&quot;Browser Emulation&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biWHEZ/btsQffSVIc3/wkD1weN2XCoq1kvmENpUoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiWHEZ%2FbtsQffSVIc3%2FwkD1weN2XCoq1kvmENpUoK%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;1006&quot; height=&quot;505&quot; data-origin-width=&quot;1006&quot; data-origin-height=&quot;505&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Browser Emulation&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;마지막으로 Run Logic에서 iteration을 5로 하여 실제로 5번 호출되는지 테스트 해봅시다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트를 실행하고 브라우저에서 localhost:8080으로 접속하여 join을 눌립니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1517&quot; data-origin-height=&quot;981&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bym51B/btsQcJanWA7/NDKmwT3j7DjlQc3MvUaa70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bym51B/btsQcJanWA7/NDKmwT3j7DjlQc3MvUaa70/img.png&quot; data-alt=&quot;스크립트 테스트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bym51B/btsQcJanWA7/NDKmwT3j7DjlQc3MvUaa70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbym51B%2FbtsQcJanWA7%2FNDKmwT3j7DjlQc3MvUaa70%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;1517&quot; height=&quot;981&quot; data-origin-width=&quot;1517&quot; data-origin-height=&quot;981&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;h2 data-ke-size=&quot;size26&quot;&gt;Controller&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Vugen 스크립트 설정이 끝났으므로 Controller를 들어갑시다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 Manual Scenario를 씁시다&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;760&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rxnfU/btsQffk5ScR/1YHPOnW2VzjCHKKfkoo5y0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rxnfU/btsQffk5ScR/1YHPOnW2VzjCHKKfkoo5y0/img.png&quot; data-alt=&quot;Controller Schedule 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rxnfU/btsQffk5ScR/1YHPOnW2VzjCHKKfkoo5y0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrxnfU%2FbtsQffk5ScR%2F1YHPOnW2VzjCHKKfkoo5y0%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;760&quot; data-origin-width=&quot;762&quot; data-origin-height=&quot;760&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Controller Schedule 설정&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;Scenario Groups : VuGen의 스크립트를 불러옵니다. 여러 스크립트를 사용할 수 있습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Scenario Schedule : Vuser를 설정합니다. 스크립트마다 다르게 설정할 수도 있고 똑같이 설정할 수도 있습니다.&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;Start Vusers를 아래와 같이 설정했다면, 총 10명의 Vuser가 생성되고, 1부터시작하여 10까지 1초간격으로 추가로 생성된다는 뜻입니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;527&quot; data-origin-height=&quot;211&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B6Ui8/btsQexsJ0go/yPr7r5LpKC3Qjjc72VRGcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B6Ui8/btsQexsJ0go/yPr7r5LpKC3Qjjc72VRGcK/img.png&quot; data-alt=&quot;Start Vusers 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B6Ui8/btsQexsJ0go/yPr7r5LpKC3Qjjc72VRGcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB6Ui8%2FbtsQexsJ0go%2FyPr7r5LpKC3Qjjc72VRGcK%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;527&quot; height=&quot;211&quot; data-origin-width=&quot;527&quot; data-origin-height=&quot;211&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Start Vusers 화면&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;(추가적으로 webhttphtml1 오른쪽 마우스 &amp;gt; runtime setting &amp;gt; pacing &amp;gt; 1초로 지정하며 각 action을 1초 간격으로 실행하게 했습니다)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1884&quot; data-origin-height=&quot;1093&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4nhWT/btsQcZRiHAy/Drq0R9wePonNeg7KLCbXSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4nhWT/btsQcZRiHAy/Drq0R9wePonNeg7KLCbXSK/img.png&quot; data-alt=&quot;Vuser 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4nhWT/btsQcZRiHAy/Drq0R9wePonNeg7KLCbXSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4nhWT%2FbtsQcZRiHAy%2FDrq0R9wePonNeg7KLCbXSK%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;1884&quot; height=&quot;1093&quot; data-origin-width=&quot;1884&quot; data-origin-height=&quot;1093&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Vuser 설정&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vuser를 홀수개만 설정하고 하나는 브라우저에서 localhost:8080/#user10으로 띄워서 실제로 잘 돌아가는지 봅시다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1889&quot; data-origin-height=&quot;982&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tkZd5/btsQc2At2Gh/6GiWanW244ArxJknC47W61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tkZd5/btsQc2At2Gh/6GiWanW244ArxJknC47W61/img.png&quot; data-alt=&quot;websocket 응답여부 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tkZd5/btsQc2At2Gh/6GiWanW244ArxJknC47W61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtkZd5%2FbtsQc2At2Gh%2F6GiWanW244ArxJknC47W61%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;1889&quot; height=&quot;982&quot; data-origin-width=&quot;1889&quot; data-origin-height=&quot;982&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;websocket 응답여부 확인&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;&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;1100&quot; data-origin-height=&quot;717&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdaK21/btsQffZHDvt/KTeiJkRBTDKUyD5YThxMRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdaK21/btsQffZHDvt/KTeiJkRBTDKUyD5YThxMRK/img.png&quot; data-alt=&quot;websocket statistics&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdaK21/btsQffZHDvt/KTeiJkRBTDKUyD5YThxMRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdaK21%2FbtsQffZHDvt%2FKTeiJkRBTDKUyD5YThxMRK%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;1100&quot; height=&quot;717&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;717&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;websocket statistics&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;이후 Loadrunner Analysis로 결과를 볼 수 있습니다 (생략)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;168&quot; data-start=&quot;27&quot; data-ke-size=&quot;size16&quot;&gt;웹소켓은 Response time이나 오류를 감지하기 힘들기 때문에 다른 방법으로 SLA 측정을 해야 합니다.&lt;br /&gt;따라서 메시지가 정상적으로 DB에 적재되는지, 로그에 누락이나 지연이 없는지를 별도로 확인하는 방식으로 성능을 검증해야 합니다.&lt;/p&gt;</description>
      <category>개발업무/개발</category>
      <author>NickTop</author>
      <guid isPermaLink="true">https://jjjjqqq.tistory.com/104</guid>
      <comments>https://jjjjqqq.tistory.com/104#entry104comment</comments>
      <pubDate>Fri, 29 Aug 2025 21:57:11 +0900</pubDate>
    </item>
    <item>
      <title>PEFT : Parameter Efficient Fine-Tuning</title>
      <link>https://jjjjqqq.tistory.com/103</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;거대모델은 pretrained 모델을 파인튜닝하는데 리소스가 많이 소요됩니다&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;또한 N개의 task별로 N개의 모델이 존재한다면 효율적이지 못합니다&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;h2 data-ke-size=&quot;size26&quot;&gt;Adapter&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Parameter-Efficient Transfer Learning for NLP : 2019&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://arxiv.org/pdf/1902.00751&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://arxiv.org/pdf/1902.00751&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1098&quot; data-origin-height=&quot;780&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/piIqi/btsOCgF6eu9/dWqF50uJjoVazatqZgRiHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/piIqi/btsOCgF6eu9/dWqF50uJjoVazatqZgRiHk/img.png&quot; data-alt=&quot;adapter architecture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/piIqi/btsOCgF6eu9/dWqF50uJjoVazatqZgRiHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpiIqi%2FbtsOCgF6eu9%2FdWqF50uJjoVazatqZgRiHk%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;484&quot; height=&quot;344&quot; data-origin-width=&quot;1098&quot; data-origin-height=&quot;780&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;adapter architecture&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;Task별로 adapter부분만 fine tuning합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 단순히 맨 마지막 layer만 fine tuning 할때보다 더 성능이 좋습니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;764&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crn1Ur/btsOCENwhfG/gZynxrXFpMp4e4oRFifZE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crn1Ur/btsOCENwhfG/gZynxrXFpMp4e4oRFifZE0/img.png&quot; data-alt=&quot;fine tuning top layers vs adapter&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crn1Ur/btsOCENwhfG/gZynxrXFpMp4e4oRFifZE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcrn1Ur%2FbtsOCENwhfG%2FgZynxrXFpMp4e4oRFifZE0%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;484&quot; height=&quot;339&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;764&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;fine tuning top layers vs adapter&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 inference에서 추가적인 adapter에 따른 latency가 있습니다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;BitFit&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://arxiv.org/pdf/2106.10199&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://arxiv.org/pdf/2106.10199&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BitFit: Simple Parameter-efficient Fine-tuning for Transformer-based Masked Language-models (2021)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2240&quot; data-origin-height=&quot;556&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRQVBT/btsOCAqNRDZ/zXTvqNRklQIA5e7Utfxny0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRQVBT/btsOCAqNRDZ/zXTvqNRklQIA5e7Utfxny0/img.png&quot; data-alt=&quot;BitFit 실험 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRQVBT/btsOCAqNRDZ/zXTvqNRklQIA5e7Utfxny0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRQVBT%2FbtsOCAqNRDZ%2FzXTvqNRklQIA5e7Utfxny0%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;2240&quot; height=&quot;556&quot; data-origin-width=&quot;2240&quot; data-origin-height=&quot;556&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;BitFit 실험 결과&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;BitFit : BIas-Term FIne-Tuning&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말 그대로 각 task별로 bias만 업데이트 시킵니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bias는 전체파라미터중 0.08%의 비중입니다. 하지만 뛰어난 성능을 보입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Base model은 Bert입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Prefix Tuning&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prefix-Tuning: Optimizing Continuous Prompts for Generation : 2021&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://arxiv.org/pdf/2101.00190&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://arxiv.org/pdf/2101.00190&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1094&quot; data-origin-height=&quot;814&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5nUNU/btsOCNQ0ie4/wHhhkad5LmZkfTkVP13hfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5nUNU/btsOCNQ0ie4/wHhhkad5LmZkfTkVP13hfk/img.png&quot; data-alt=&quot;prefix tuning methodology&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5nUNU/btsOCNQ0ie4/wHhhkad5LmZkfTkVP13hfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5nUNU%2FbtsOCNQ0ie4%2FwHhhkad5LmZkfTkVP13hfk%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;490&quot; height=&quot;365&quot; data-origin-width=&quot;1094&quot; data-origin-height=&quot;814&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;prefix tuning methodology&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 레이어의 앞 쪽 N개의 토큰만 파인튜닝합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;base모델은 BART와 GPT입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Prompt Tuning&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The Power of Scale for Parameter-Efficient Prompt Tuning: 2021&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://arxiv.org/pdf/2104.08691&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://arxiv.org/pdf/2104.08691&lt;/a&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;1220&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xZGfA/btsOA0q0yTi/kZ9FmJSMaYhltnPtd1s8K1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xZGfA/btsOA0q0yTi/kZ9FmJSMaYhltnPtd1s8K1/img.png&quot; data-alt=&quot;prompt tuning architecture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xZGfA/btsOA0q0yTi/kZ9FmJSMaYhltnPtd1s8K1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxZGfA%2FbtsOA0q0yTi%2FkZ9FmJSMaYhltnPtd1s8K1%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;1220&quot; height=&quot;700&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;prompt tuning architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pretrained Model은 건드리지 않습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 task마다 추가적인 k개의 토큰을 입력 앞에 넣습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적인 k개의 토큰만 파인튜닝합니다. 논문에서는 5개의 토큰만 써도 충분하다고 합니다 (20,480 추가 parameter)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;논문상에서 직접적으로 prefix tuning에 비해 parameter efficient라고 하는 반면 정확도에 대한 언급은 없습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;base모델은 T5입니다&lt;/p&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;h2 data-ke-size=&quot;size26&quot;&gt;LoRA&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://arxiv.org/pdf/2106.09685&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://arxiv.org/pdf/2106.09685&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS (2021)&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;484&quot; data-origin-height=&quot;428&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0ifHx/btsOALAK0nN/bXraSixT4SfOgyzZ165Fbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0ifHx/btsOALAK0nN/bXraSixT4SfOgyzZ165Fbk/img.png&quot; data-alt=&quot;LoRA architecture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0ifHx/btsOALAK0nN/bXraSixT4SfOgyzZ165Fbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0ifHx%2FbtsOALAK0nN%2FbXraSixT4SfOgyzZ165Fbk%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;309&quot; height=&quot;273&quot; data-origin-width=&quot;484&quot; data-origin-height=&quot;428&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;LoRA architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pretrained weight는 고정시키고 A와 B만 학습합니다 (W_new = W + AB)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설계에 따라 r을 변경하면서 tunable parameter수를 조절할 수 있습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점은 다른 방법들과 달리 inference에서 latency가 아예 없습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 W_new = W + AB를 미리 계산할 수 있기 때문입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 task에서 full fine tuning과 비교했을떄 퍼포펀스 drop이 거의 없거나 LoRA가 조금 더 앞섭니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;PEQA&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Memory-Efficient Fine-Tuning of Compressed Large Language Models via sub-4-bit Integer Quantization : 2023&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://arxiv.org/pdf/2305.14152&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://arxiv.org/pdf/2305.14152&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;Quantization을 고려합시다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LoRA는 learnable parameter수를 줄였지만 어쨌든 학습을 위해서 전체 W는 메모리에 올라가있어야 합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;QAT : 전체 파라미터를 학습해야 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PEFT &amp;rarr; PTQ : PEFT 중에는(학습중에는) quantization으로 인한 memory 감소 효과를 볼순 없음 (물론 inference단계에선 경량화 효과 있음)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PTQ &amp;rarr; PEFT : 학습중에는 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;quantization으로 인한 memory 감소 효과를&lt;span&gt;&amp;nbsp; 볼 수 있지만, inference 단계에서 메모리 절감 효과를 볼 수 없음(?)&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;2072&quot; data-origin-height=&quot;678&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/14Bb0/btsOBdDGPLm/DoahhlvmUqkaQvRhzgqii0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/14Bb0/btsOBdDGPLm/DoahhlvmUqkaQvRhzgqii0/img.png&quot; data-alt=&quot;PEQA architecture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/14Bb0/btsOBdDGPLm/DoahhlvmUqkaQvRhzgqii0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F14Bb0%2FbtsOBdDGPLm%2FDoahhlvmUqkaQvRhzgqii0%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;2072&quot; height=&quot;678&quot; data-origin-width=&quot;2072&quot; data-origin-height=&quot;678&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;PEQA architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;quantization 후 scale만 학습함&lt;/p&gt;</description>
      <category>인공지능/최적화</category>
      <author>NickTop</author>
      <guid isPermaLink="true">https://jjjjqqq.tistory.com/103</guid>
      <comments>https://jjjjqqq.tistory.com/103#entry103comment</comments>
      <pubDate>Sat, 14 Jun 2025 19:13:59 +0900</pubDate>
    </item>
    <item>
      <title>DynamicViT: Efficient Vision Transformers with Dynamic Token Sparsification</title>
      <link>https://jjjjqqq.tistory.com/102</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Overview&lt;/h2&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;a href=&quot;https://arxiv.org/pdf/2106.02034&quot;&gt;https://arxiv.org/pdf/2106.02034&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;DynamicViT는 토큰 사이즈를 줄여 네트워크를 효율화하는 방식입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 이미지패치가 결과에 어느정도의 영향을 주는지 파악하는 MLP를 만들어 영향이 적은 패치를 사용하지 않도록 합니다&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;1430&quot; data-origin-height=&quot;808&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AYrTy/btsN1tmPrCw/SoK166kpJYbqT8CeWxQqD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AYrTy/btsN1tmPrCw/SoK166kpJYbqT8CeWxQqD0/img.png&quot; data-alt=&quot;overview of Dynamic ViT&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AYrTy/btsN1tmPrCw/SoK166kpJYbqT8CeWxQqD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAYrTy%2FbtsN1tmPrCw%2FSoK166kpJYbqT8CeWxQqD0%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;1430&quot; height=&quot;808&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;808&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;overview of Dynamic ViT&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Methodology&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Prediction Module&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\hat{D}&amp;nbsp;\in&amp;nbsp;(0,1)^N$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;N : number of Token&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C : size of 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;D는 현재 이미지 패치가 마스킹된 결과값입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\textbf{z}^{local}&amp;nbsp;=&amp;nbsp;MLP(\textbf{x})&amp;nbsp;\in&amp;nbsp;\mathbb{R}^{N&amp;nbsp;\times&amp;nbsp;C'}$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C' = C/2&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\textbf{z}^{global}&amp;nbsp;=&amp;nbsp;Agg(\textbf{z}^{local},\hat{D})&amp;nbsp;\in&amp;nbsp;\mathbb{R}^{C'}$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agg는 단순히 평균을 낸것입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;local은 각 패치의 정보이고 global은 전체 이미지에 대한 정보입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 값을 다시 MLP에 넣어 값이 낮은 토큰을 drop합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\textbf{z}_i = [\textbf{z}_i^{local},\textbf{z}_i^{global}]$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\textbf{\pi} = Softmax(MLP(\textbf(z)) \in mathbb{R}^{N \times 2}$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 토큰에 대한 결과는 [P(keep), P(prune)] i.e.) [0.88, 0.12] 로 나옵니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;local을 얻는데 쓰인 MLP&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;LayerNorm&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;&amp;rarr;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;Linear&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;C&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;,&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;C/&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;)&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;&amp;rarr;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;GELU&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;keep, prune확률을 구하는데 쓰인 MLP&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;Linear&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;C&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;,&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;C/&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;)&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;&amp;rarr;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;GELU&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;&amp;rarr;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;Linear&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;C/&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;,&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;C/&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;)&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;&amp;rarr;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;GELU&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;&amp;rarr;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;Linear&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;C/&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;,&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;)&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;&amp;rarr;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;Softmax&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;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;참고로, 모든 stage별로 MLP는 동일합니다&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;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;pruning은 모든 layer에 적용되진 않았고 3개의 stage로 했을때 성능이 좋았다고 합니다&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;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;[&amp;rho;, &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&amp;rho;^2, &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&amp;rho;^3]로 점진적으로 pruning을 적용했으며 4개이상의 stage에서 gain은 없었다고 합니다&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1180&quot; data-origin-height=&quot;348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pVNwa/btsN03h2I9e/fGrC02CmNAQKMm3gEHV7WK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pVNwa/btsN03h2I9e/fGrC02CmNAQKMm3gEHV7WK/img.png&quot; data-alt=&quot;stage별 실험결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pVNwa/btsN03h2I9e/fGrC02CmNAQKMm3gEHV7WK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpVNwa%2FbtsN03h2I9e%2FfGrC02CmNAQKMm3gEHV7WK%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;1180&quot; height=&quot;348&quot; data-origin-width=&quot;1180&quot; data-origin-height=&quot;348&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;stage별 실험결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Attention Masking&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D를 0과 1로만 정의하면 미분불가능합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;Gumbel-Softmax를 적용했습니다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D = &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Gumbel-Softmax(&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&amp;pi;)&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;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;또다른 문제가 있는데 각 이미지 토큰에 마스킹을 적용하더라도 self-attention을 구할때 softmax를 통과하면 마스킹한 효과를 주지 못합니다&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;i.e)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;softmax([-2,-4,1,-1,1] &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;⊙&lt;/span&gt; &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;[1,1,0,0,1] ) = &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;[0.0278, 0.00376, 0.2053, 0.2053, 0.5580]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 masking을 했지만 softmax를 통과하면 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;따라서 기존의 Attention ($A=softmax(\frac{QK^T}{\sqrt{C}})$)을 다음과 같이 변경합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;${\begin{aligned}&lt;br /&gt;\mathbf{P}&amp;nbsp;&amp;amp;=&amp;nbsp;\mathbf{QK}^T&amp;nbsp;/&amp;nbsp;\sqrt{C}&amp;nbsp;\in&amp;nbsp;\mathbb{R}^{N\times&amp;nbsp;N},&amp;nbsp;\\&lt;br /&gt;\mathbf{G}_{ij}&amp;nbsp;&amp;amp;=&lt;br /&gt;\begin{cases}&lt;br /&gt;1,&amp;nbsp;&amp;amp;&amp;nbsp;i&amp;nbsp;=&amp;nbsp;j,&amp;nbsp;\\[6pt]&lt;br /&gt;\hat{\mathbf{D}}_{j},&amp;nbsp;&amp;amp;&amp;nbsp;i&amp;nbsp;\neq&amp;nbsp;j.&lt;br /&gt;\end{cases}&amp;nbsp;\\[10pt]&lt;br /&gt;\tilde{\mathbf{A}}_{ij}&amp;nbsp;&amp;amp;=&amp;nbsp;\frac{\exp(\mathbf{P}_{ij})&amp;nbsp;\mathbf{G}_{ij}}{\sum_{k=1}^{N}\exp(\mathbf{P}_{ik})&amp;nbsp;\mathbf{G}_{ik}},&lt;br /&gt;\end{aligned}&lt;br /&gt;}$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$A_{i,j}$의 의미가 i토큰을 참조할때 j가 얼마나 중요한지를 나타냅니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;j번째 토큰을 프루닝했다면 중요도가 0이 되는 것을 위 식에서 알 수 있습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pruning 학습할때는 연산을 위해 각 layer에서 전체 number of token을 유지하고, inference할때는 실제로 입력 토큰을 없앱니다&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;Loss&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 4개의 loss를 씁니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 prediction 계산&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\mathcal{L}_{\text{cls}}&amp;nbsp;=&amp;nbsp;\text{CrossEntropy}(y,&amp;nbsp;\hat{y})$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 토큰을 teacher의 token과 똑같이 만듬 (왜 하는건진 잘 모르겠음)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\mathcal{L}_{\text{distill}}&amp;nbsp;=&amp;nbsp;\frac{1}{\sum_{b=1}^{B}&amp;nbsp;\sum_{i=1}^{N}&amp;nbsp;\hat{\mathbf{D}}^{b,S}_i}\sum_{b=1}^{B}&amp;nbsp;\sum_{i=1}^{N}&amp;nbsp;\hat{\mathbf{D}}^{b,S}_i&amp;nbsp;\left(&amp;nbsp;t_i&amp;nbsp;-&amp;nbsp;t'_i&amp;nbsp;\right)^2$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;teacher model과 loss를 일치시키기 위한 KL divergence&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\mathcal{L}_{\text{KL}}&amp;nbsp;=&amp;nbsp;\text{KL}(\mathbf{y}&amp;nbsp;\parallel&amp;nbsp;\mathbf{y}')$&lt;/p&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;nbsp;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&amp;rho;_ratio Loss&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\mathcal{L}_{\text{ratio}}&amp;nbsp;=&amp;nbsp;\frac{1}{BS}&amp;nbsp;\sum_{b=1}^{B}&amp;nbsp;\sum_{s=1}^{S}&amp;nbsp;\left(&amp;nbsp;\rho^{(s)}&amp;nbsp;-&amp;nbsp;\frac{1}{N}&amp;nbsp;\sum_{i=1}^{N}&amp;nbsp;\hat{\mathbf{D}}^{b,s}_i&amp;nbsp;\right)^2$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Visualization&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1624&quot; data-origin-height=&quot;1410&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuwa7R/btsN28g4yev/ACSOaDDjHZ6iAbADwI4kqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuwa7R/btsN28g4yev/ACSOaDDjHZ6iAbADwI4kqk/img.png&quot; data-alt=&quot;실험결과에 대한 visualization&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuwa7R/btsN28g4yev/ACSOaDDjHZ6iAbADwI4kqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcuwa7R%2FbtsN28g4yev%2FACSOaDDjHZ6iAbADwI4kqk%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;1624&quot; height=&quot;1410&quot; data-origin-width=&quot;1624&quot; data-origin-height=&quot;1410&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실험결과에 대한 visualization&lt;/figcaption&gt;
&lt;/figure&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;</description>
      <category>인공지능</category>
      <author>NickTop</author>
      <guid isPermaLink="true">https://jjjjqqq.tistory.com/102</guid>
      <comments>https://jjjjqqq.tistory.com/102#entry102comment</comments>
      <pubDate>Sun, 18 May 2025 16:33:21 +0900</pubDate>
    </item>
    <item>
      <title>Kernel Ridge Regression</title>
      <link>https://jjjjqqq.tistory.com/101</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Ridge Regression&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\underset{\beta}{min}&amp;nbsp;||Y-\beta&amp;nbsp;X||&amp;nbsp;+\lambda&amp;nbsp;||\beta||^2$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 regression식에 정규화를 더한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규화 수식은 베타가 작은 값을 유지하게 만든다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;베타가 작아지면 작은 x의 변화로 y가 크게 변하지 않는다&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;h2 data-ke-size=&quot;size26&quot;&gt;Kernel Ridge Regression&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Primal Form&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;linear한 수식이 아닌 일반적인 식에 대해 생각해보자 $y&amp;nbsp;=&amp;nbsp;\phi(x)$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\underset&amp;nbsp;{\omega}{min}&amp;nbsp;||Y&amp;nbsp;-&amp;nbsp;\Phi&amp;nbsp;\omega||&amp;nbsp;+&amp;nbsp;\lambda&amp;nbsp;||\omega||^2$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Y는 1*n 벡터입니다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\Phi&amp;nbsp;\in&amp;nbsp;\mathbb{R}^{n&amp;nbsp;\times&amp;nbsp;D}$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\omega \in&amp;nbsp;\mathbb{R}^{D}$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;n은 데이터셋 개수, D는 특성 개수 (y = [w1,w2,w3,w4] (내적) [x1,x2,x3*x1,x1*x2]으로 풀 수 있고 이때 D=4)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미분가능한 함수가 최대 또는 최소를 가지는 지점은 미분이 0이 될때임 (2차함수를 생각하면 쉬움)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$L = ||Y&amp;nbsp;-&amp;nbsp;\Phi&amp;nbsp;\omega||&amp;nbsp;+&amp;nbsp;\lambda&amp;nbsp;||\omega||^2$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\frac {\partial L}{\partial w} = -2\Phi^T(Y-\Phi w) + 2\lambda \omega =0$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위를 풀면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\omega = (\Phi^T\Phi + \lambda I)^{-1}\Phi^TY$&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;Dual Form&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 kernel ridge regression은 dual form을 가리키는 말이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Woodbury_matrix_identity&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://en.wikipedia.org/wiki/Woodbury_matrix_identity&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;woodbury 행렬 항등식을 쓰면 $\omega$를 다음과 같이 정의할 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\omega = \Phi^T(\Phi \Phi^T + \lambda I)^{-1}Y$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 예측식에 대입하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$y = \phi(x)^T \omega =(&amp;nbsp;\Phi&amp;nbsp;\phi(x))^T(\Phi \Phi^T + \lambda I)^{-1}Y$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 kernel을 다음과 같이 정의할 수 있습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$K = \Phi \Phi^T$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$K_{ij} = k(x_i,x_j)$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;kernel은 위와같이 두 입력값을 통한 수식입니다&lt;/p&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;$\hat y(x) = [k(x_1,x),k(x_2,x),...,k(x_n,x)]^T(K+\lambda I)^{-1}Y$&lt;/p&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;고차원 특징을 명시적으로 설계하지 않아도 됨&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D가 너무 커지면 연산량이 너무 많아집니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;K의 차원은 n*n이기 때문에 feature에 weight를 곱한 모델을 만들지 않아도 됩니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, feature를 설계하는 것이 아닌 kernel을 설계하는 것입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로는 RBF가 있습니다&lt;/p&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;a href=&quot;https://en.wikipedia.org/wiki/Radial_basis_function_kernel&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://en.wikipedia.org/wiki/Radial_basis_function_kernel&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1746516043644&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Radial basis function kernel - Wikipedia&quot; data-og-description=&quot;From Wikipedia, the free encyclopedia Machine learning kernel function In machine learning, the radial basis function kernel, or RBF kernel, is a popular kernel function used in various kernelized learning algorithms. In particular, it is commonly used in &quot; data-og-host=&quot;en.wikipedia.org&quot; data-og-source-url=&quot;https://en.wikipedia.org/wiki/Radial_basis_function_kernel&quot; data-og-url=&quot;https://en.wikipedia.org/wiki/Radial_basis_function_kernel&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Radial_basis_function_kernel&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://en.wikipedia.org/wiki/Radial_basis_function_kernel&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Radial basis function kernel - Wikipedia&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;From Wikipedia, the free encyclopedia Machine learning kernel function In machine learning, the radial basis function kernel, or RBF kernel, is a popular kernel function used in various kernelized learning algorithms. In particular, it is commonly used in&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;en.wikipedia.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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>인공지능</category>
      <author>NickTop</author>
      <guid isPermaLink="true">https://jjjjqqq.tistory.com/101</guid>
      <comments>https://jjjjqqq.tistory.com/101#entry101comment</comments>
      <pubDate>Tue, 6 May 2025 16:21:13 +0900</pubDate>
    </item>
    <item>
      <title>Dataset Distillation</title>
      <link>https://jjjjqqq.tistory.com/100</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://arxiv.org/pdf/1811.10959&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://arxiv.org/pdf/1811.10959&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1710&quot; data-origin-height=&quot;706&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rKmMi/btsNF51wsxa/lbfWWUS5UqqNxWMMkOs20K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rKmMi/btsNF51wsxa/lbfWWUS5UqqNxWMMkOs20K/img.png&quot; data-alt=&quot;MNIST와 cifar10 distillation 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rKmMi/btsNF51wsxa/lbfWWUS5UqqNxWMMkOs20K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrKmMi%2FbtsNF51wsxa%2FlbfWWUS5UqqNxWMMkOs20K%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;1710&quot; height=&quot;706&quot; data-origin-width=&quot;1710&quot; data-origin-height=&quot;706&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;MNIST와 cifar10 distillation 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dataset으로부터 가짜이미지를 만들어 적은 이미지로도 모델을 학습합니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1664&quot; data-origin-height=&quot;434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nGFTM/btsNIwWSdVI/jFAuH9n1XjFYxZiWHd3bu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nGFTM/btsNIwWSdVI/jFAuH9n1XjFYxZiWHd3bu0/img.png&quot; data-alt=&quot;model poisioning&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nGFTM/btsNIwWSdVI/jFAuH9n1XjFYxZiWHd3bu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnGFTM%2FbtsNIwWSdVI%2FjFAuH9n1XjFYxZiWHd3bu0%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;1664&quot; height=&quot;434&quot; data-origin-width=&quot;1664&quot; data-origin-height=&quot;434&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;model poisioning&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 label(plane)을 attack하는 이미지를 만들어 gradient step하나만 돌려 모델의 정확도를 낮출 수도 있습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Approach&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\theta_1&amp;nbsp;=&amp;nbsp;\theta_0&amp;nbsp;-&amp;nbsp;\tilde{\eta}&amp;nbsp;\nabla_{\theta_0}&amp;nbsp;\ell(\tilde{\mathbf{x}},&amp;nbsp;\theta_0) $ : weight는 가짜 데이터 (synthetic data)로만 학습됩니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\tilde{\mathbf{x}}^*,&amp;nbsp;\tilde{\eta}^*&amp;nbsp;=&amp;nbsp;\arg\min_{\tilde{\mathbf{x}},&amp;nbsp;\tilde{\eta}}&amp;nbsp;\mathcal{L}(\tilde{\mathbf{x}},&amp;nbsp;\tilde{\eta};&amp;nbsp;\theta_0)&amp;nbsp;=&amp;nbsp;\arg\min_{\tilde{\mathbf{x}},&amp;nbsp;\tilde{\eta}}&amp;nbsp;\ell&amp;nbsp;(\mathbf{x},&amp;nbsp;\theta_1)&amp;nbsp;=&amp;nbsp;\arg\min_{\tilde{\mathbf{x}},&amp;nbsp;\tilde{\eta}}&amp;nbsp;\ell(\mathbf{x},&amp;nbsp;\theta_0&amp;nbsp;-&amp;nbsp;\tilde{\eta}&amp;nbsp;\nabla_{\theta_0}&amp;nbsp;\ell(\tilde{\mathbf{x}},&amp;nbsp;\theta_0))&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;$\arg\min_{\tilde{\mathbf{x}},&amp;nbsp;\tilde{\eta}}&amp;nbsp;\ell&amp;nbsp;(\mathbf{x},&amp;nbsp;\theta_1)$ : 실제 데이터 x의 loss가 줄어드는 방향으로 synthetic data와 learning rate를 선택합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;계산&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gradient를 계산하는 방법을 살펴봅시다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;${\begin{align}&amp;nbsp;&lt;br /&gt;\frac&amp;nbsp;{\partial&amp;nbsp;L}{\partial&amp;nbsp;\tilde{x}}&amp;nbsp;&amp;amp;=&amp;nbsp;&amp;nbsp;\frac{\partial&amp;nbsp;L}{\partial&amp;nbsp;\theta_1}&amp;nbsp;\frac{\partial&amp;nbsp;\theta_1}{\partial&amp;nbsp;\tilde{x}}&amp;nbsp;\\&lt;br /&gt;&amp;amp;=&amp;nbsp;\frac{\partial&amp;nbsp;L}{\partial&amp;nbsp;\theta_1}&amp;nbsp;(\frac&amp;nbsp;{\partial}{\partial&amp;nbsp;\tilde&amp;nbsp;x}(\theta_0&amp;nbsp;-&amp;nbsp;\tilde&amp;nbsp;\eta&amp;nbsp;\nabla&amp;nbsp;\ell&amp;nbsp;(\tilde&amp;nbsp;x,&amp;nbsp;\theta_0)))&amp;nbsp;\\&lt;br /&gt;&amp;amp;=&amp;nbsp;\frac{\partial&amp;nbsp;L}{\partial&amp;nbsp;\theta_1}&amp;nbsp;&amp;nbsp;(\frac{\partial&amp;nbsp;\theta_0}{\partial&amp;nbsp;\tilde&amp;nbsp;x}&amp;nbsp;-&amp;nbsp;\tilde&amp;nbsp;\eta&amp;nbsp;\frac{\partial^2&amp;nbsp;\ell&amp;nbsp;(\tilde&amp;nbsp;x,&amp;nbsp;\theta_0))}{\partial&amp;nbsp;\tilde&amp;nbsp;x&amp;nbsp;\partial&amp;nbsp;\theta_0})&amp;nbsp;\\&lt;br /&gt;&amp;amp;=-\tilde&amp;nbsp;\eta&amp;nbsp;\frac{\partial&amp;nbsp;L}{\partial&amp;nbsp;\theta_1}&amp;nbsp;\frac{\partial^2&amp;nbsp;\ell&amp;nbsp;(\tilde&amp;nbsp;x,&amp;nbsp;\theta_0))}{\partial&amp;nbsp;\tilde&amp;nbsp;x&amp;nbsp;\partial&amp;nbsp;\theta_0}&lt;br /&gt;\end{align}&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;approach에서 weight가 한번만 업데이트 되었지만, 실제로는 여러번 업데이트 됩니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\theta_t$ 상태에서 $\theta_T$가 되었다고 합시다 (T-t번 업데이트됨)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 한번 업데이트할때 s만큼의 데이터를 쓴다고 합시다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;${\theta_{s+1}&amp;nbsp;=&amp;nbsp;\theta_s&amp;nbsp;-&amp;nbsp;\tilde{\eta}_s \nabla_{\theta_s}&amp;nbsp;\ell(\tilde{\mathbf{x}},&amp;nbsp;\theta_s)}$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미분하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;${\frac&amp;nbsp;{\partial&amp;nbsp;\theta_{s+1}}{\partial&amp;nbsp;\theta_s}&amp;nbsp;=&amp;nbsp;I&amp;nbsp;-&amp;nbsp;\tilde&amp;nbsp;\eta_s&amp;nbsp;H_s}$&lt;/p&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;${\begin{align}&lt;br /&gt;\frac&amp;nbsp;{\partial&amp;nbsp;L}{\partial&amp;nbsp;\tilde&amp;nbsp;x}&amp;nbsp;&amp;amp;=&amp;nbsp;\frac&amp;nbsp;{\partial&amp;nbsp;L}{\partial&amp;nbsp;&amp;nbsp;\theta_T}\frac&amp;nbsp;{\partial&amp;nbsp;\theta_T}{\partial&amp;nbsp;&amp;nbsp;\theta_{T-1}}\frac&amp;nbsp;{\partial&amp;nbsp;\theta_{T-1}}{\partial&amp;nbsp;&amp;nbsp;\theta_{T-2}}...\frac&amp;nbsp;{\partial&amp;nbsp;\theta_{t+2}}{\partial&amp;nbsp;&amp;nbsp;\theta_{t+1}}&amp;nbsp;\frac&amp;nbsp;{\partial&amp;nbsp;\theta_{t+1}}{\partial&amp;nbsp;&amp;nbsp;\tilde&amp;nbsp;x}&amp;nbsp;\\&lt;br /&gt;&amp;amp;=\frac&amp;nbsp;{\partial&amp;nbsp;L}{\partial&amp;nbsp;&amp;nbsp;\theta_T}&amp;nbsp;\prod_{s=t+1}^{T}(I-\tilde\eta_sH_s)&amp;nbsp;(-\tilde\eta_t \frac{\partial^2&amp;nbsp;\ell&amp;nbsp;(\tilde&amp;nbsp;x,&amp;nbsp;\theta_t))}{\partial&amp;nbsp;\tilde&amp;nbsp;x&amp;nbsp;\partial&amp;nbsp;\theta_t})&lt;br /&gt;\end{align}}$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실험과정에서 위와 같이 얻은 synthetic data를 다른 weight initialization에 사용했을때 문제가 있었다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 여러개의 weight initialization에 대해서 synthetic data를 업데이트 했다고 합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;N개의 weight initialization에 대해 synthetic data를 업데이트 했다고 합시다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;논문에서 최종 loss는 아래와 같이 계산됩니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;${\begin{align}&lt;br /&gt;\frac&amp;nbsp;{\partial&amp;nbsp;L}{\partial&amp;nbsp;\tilde&amp;nbsp;x}&amp;nbsp;&lt;br /&gt;&amp;amp;=\sum^N_{i=1}&amp;nbsp;\frac&amp;nbsp;{\partial&amp;nbsp;L}{\partial&amp;nbsp;&amp;nbsp;\theta_T^{(i)}}&amp;nbsp;\prod_{s=t+1}^{T}(I-\tilde\eta_s^{(i)}H_s^{(i)})&amp;nbsp;(-\tilde\eta_t^{(i)}&amp;nbsp;\frac{\partial^2&amp;nbsp;\ell&amp;nbsp;(\tilde&amp;nbsp;x,&amp;nbsp;\theta_t^{(i)}))}{\partial&amp;nbsp;\tilde&amp;nbsp;x&amp;nbsp;\partial&amp;nbsp;\theta_t^{(i)}})&amp;nbsp;\\&lt;br /&gt;\end{align}}$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\tilde \eta$도 학습되는 파라미터입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비슷한 방법으로 계산하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;${\frac&amp;nbsp;{\partial&amp;nbsp;L}{\partial&amp;nbsp;\tilde&amp;nbsp;\eta_t^*}=\frac&amp;nbsp;{\partial&amp;nbsp;L}{\partial&amp;nbsp;&amp;nbsp;\theta_T}&amp;nbsp;\prod_{s=t+1}^{T}(I-\tilde\eta_sH_s)&amp;nbsp;(-&amp;nbsp;\frac{\partial&amp;nbsp;\ell&amp;nbsp;(\tilde&amp;nbsp;x,&amp;nbsp;\theta_t))}{&amp;nbsp;\partial&amp;nbsp;\theta_t})}$&lt;/p&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;1782&quot; data-origin-height=&quot;802&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Mvcta/btsNHMFRDd1/TebmLUj6cx5ZTQbqScckEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Mvcta/btsNHMFRDd1/TebmLUj6cx5ZTQbqScckEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Mvcta/btsNHMFRDd1/TebmLUj6cx5ZTQbqScckEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMvcta%2FbtsNHMFRDd1%2FTebmLUj6cx5ZTQbqScckEK%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;1782&quot; height=&quot;802&quot; data-origin-width=&quot;1782&quot; data-origin-height=&quot;802&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>인공지능</category>
      <author>NickTop</author>
      <guid isPermaLink="true">https://jjjjqqq.tistory.com/100</guid>
      <comments>https://jjjjqqq.tistory.com/100#entry100comment</comments>
      <pubDate>Thu, 1 May 2025 15:01:31 +0900</pubDate>
    </item>
  </channel>
</rss>