<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>rem1004 님의 블로그</title>
    <link>https://rem1004.tistory.com/</link>
    <description>rem1004 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Sat, 5 Sep 2026 10:24:35 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>rem1004</managingEditor>
    <image>
      <title>rem1004 님의 블로그</title>
      <url>https://tistory1.daumcdn.net/tistory/8961512/attach/72fca63a5674456eb6fab7cd05d3eed3</url>
      <link>https://rem1004.tistory.com</link>
    </image>
    <item>
      <title>Confusion Matrix를 왜 사용할까? Accuracy의 함정을 숫자 5 분류로 이해하기</title>
      <link>https://rem1004.tistory.com/52</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;분류 모델을 만들고 &lt;code&gt;Accuracy&lt;/code&gt;가 98%라는 결과를 얻었다고 하자. 이 모델은 정말 좋은 모델일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반드시 그렇지는 않다. 전체 데이터의 99%가 정상이고 1%만 이상인 상황에서 모든 데이터를 정상이라고 예측해도 &lt;code&gt;Accuracy&lt;/code&gt;는 99%다. 하지만 이 모델은 실제 이상 사례를 단 한 건도 찾지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 &lt;b&gt;몇 개를 맞혔는가뿐 아니라 무엇을 어떻게 틀렸는가&lt;/b&gt;를 확인하기 위해 사용하는 표가 &lt;code&gt;Confusion Matrix&lt;/code&gt;다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Confusion Matrix&lt;/code&gt;를 왜 사용하는가?&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TN&lt;/code&gt;, &lt;code&gt;FP&lt;/code&gt;, &lt;code&gt;FN&lt;/code&gt;, &lt;code&gt;TP&lt;/code&gt;는 어떻게 구분하는가?&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Accuracy&lt;/code&gt;, &lt;code&gt;Precision&lt;/code&gt;, &lt;code&gt;Recall&lt;/code&gt;, &lt;code&gt;Specificity&lt;/code&gt;, &lt;code&gt;F1 Score&lt;/code&gt;는 무엇이 다른가?&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Decision Threshold&lt;/code&gt;를 바꾸면 왜 &lt;code&gt;Precision&lt;/code&gt;과 &lt;code&gt;Recall&lt;/code&gt;이 달라지는가?&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[[391, 0], [6, 53]]&lt;/code&gt;은 어떻게 해석하는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 먼저 문제를 한 문장으로 정리하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수업에서는 &lt;code&gt;scikit-learn&lt;/code&gt;의 &lt;code&gt;digits&lt;/code&gt; 데이터를 사용한다. 이 데이터에는 손으로 쓴 숫자 0부터 9까지의 이미지 1,797장이 들어 있다. 각 이미지는 8&amp;times;8 크기이며, 모델에는 64개의 숫자로 펼쳐서 입력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래는 0부터 9까지 열 종류를 구분할 수 있는 데이터지만, 이번 수업에서는 질문을 더 단순하게 바꾼다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;이 이미지는 숫자 5인가?

5이면       &amp;rarr; Positive Class = 1
5가 아니면  &amp;rarr; Negative Class = 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;code&gt;Binary Classification&lt;/code&gt;이라고 한다. &lt;code&gt;Positive&lt;/code&gt;는 좋다는 뜻이 아니라 &lt;b&gt;우리가 찾아내기로 정한 대상&lt;/b&gt;이라는 뜻이다. 이 예제에서는 숫자 5가 &lt;code&gt;Positive&lt;/code&gt;다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;from sklearn.datasets import load_digits

digits = load_digits()
x = digits.data
y = (digits.target == 5).astype(int)

print(x.shape)  # (1797, 64)
print(y[:10])   # 5이면 1, 나머지는 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수 이름을 수업 코드와 맞추면 다음과 같이 써도 같다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;data = digits.data
target = (digits.target == 5).astype(int)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 확인할 것은 &lt;code&gt;Class Distribution&lt;/code&gt;이다.&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;import numpy as np

unique, counts = np.unique(target, return_counts=True)
print(dict(zip(unique, counts)))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자 5는 전체의 약 10%이고, 5가 아닌 숫자는 약 90%다. 두 Class의 개수가 크게 다르므로 &lt;code&gt;Class Imbalance&lt;/code&gt;가 존재한다. 바로 이 때문에 &lt;code&gt;Accuracy&lt;/code&gt; 하나만으로 평가하면 위험하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Accuracy만 보면 왜 부족할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Accuracy&lt;/code&gt;는 전체 예측 중 맞힌 비율이다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;Accuracy = 맞힌 개수 / 전체 개수&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검증 데이터 450개 중 391개가 숫자 5가 아니고 59개가 숫자 5라고 하자. 아무것도 학습하지 않고 모든 이미지를 &amp;ldquo;5가 아니다&amp;rdquo;라고만 예측해도 391개를 맞힌다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Accuracy = 391 / 450
         &amp;asymp; 0.8689
         &amp;asymp; 86.9%&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;86.9%라는 숫자만 보면 제법 좋아 보인다. 하지만 실제 숫자 5는 한 개도 찾지 못했다. 이 모델은 &amp;ldquo;숫자 5 찾기&amp;rdquo;라는 목적에는 쓸모가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수업에서 &lt;code&gt;DummyClassifier&lt;/code&gt;를 만든 이유가 바로 이것이다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;from sklearn.dummy import DummyClassifier

dummy_model = DummyClassifier(strategy=&quot;most_frequent&quot;)
dummy_model.fit(x_train, y_train)
dummy_pred = dummy_model.predict(x_valid)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;strategy=&quot;most_frequent&quot;&lt;/code&gt;는 Training Data에서 가장 많은 Class만 계속 예측한다. 성능이 낮을 것이 예상되는 단순한 기준 모델을 &lt;code&gt;Baseline Model&lt;/code&gt;이라고 한다. 학습한 모델은 최소한 이 기준보다 나아야 한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Accuracy는 높지만 숫자 5를 하나도 찾지 못하는 DummyClassifier와 SGDClassifier 비교&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;confusion-matrix-figure-03.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;650&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GXHMk/dJMcaa0UQap/aK5wMZlq5eC6TiFdHKtSuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GXHMk/dJMcaa0UQap/aK5wMZlq5eC6TiFdHKtSuK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GXHMk/dJMcaa0UQap/aK5wMZlq5eC6TiFdHKtSuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGXHMk%2FdJMcaa0UQap%2FaK5wMZlq5eC6TiFdHKtSuK%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;1200&quot; height=&quot;650&quot; data-filename=&quot;confusion-matrix-figure-03.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;650&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 다음과 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Accuracy&lt;/code&gt;는 전체 정답률만 알려 준다. 어떤 Class를 놓쳤는지, 어떤 종류의 오류를 냈는지는 알려 주지 않는다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Confusion Matrix란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Confusion Matrix&lt;/code&gt;는 &lt;b&gt;Actual Label&lt;/b&gt;과 &lt;b&gt;Predicted Label&lt;/b&gt;의 조합을 세어 놓은 표다. Binary Classification에서는 네 칸으로 구성된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Actual과 Predicted를 기준으로 구분한 TN, FP, FN, TP&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;confusion-matrix-figure-01.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;650&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZidOj/dJMcaiEHnTL/4QgtebSWoKmsxVjCQcwwbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZidOj/dJMcaiEHnTL/4QgtebSWoKmsxVjCQcwwbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZidOj/dJMcaiEHnTL/4QgtebSWoKmsxVjCQcwwbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZidOj%2FdJMcaiEHnTL%2F4QgtebSWoKmsxVjCQcwwbk%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;1200&quot; height=&quot;650&quot; data-filename=&quot;confusion-matrix-figure-01.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;650&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;scikit-learn&lt;/code&gt;의 &lt;code&gt;confusion_matrix()&lt;/code&gt;는 행에 &lt;code&gt;Actual&lt;/code&gt;, 열에 &lt;code&gt;Predicted&lt;/code&gt;를 놓는다.&lt;/p&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;                         Predicted
                    Negative   Positive
Actual Negative        TN         FP
Actual Positive        FN         TP&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반환되는 배열 순서는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[[TN, FP],
 [FN, TP]]&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;True와 False는 무엇을 뜻할까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;True&lt;/code&gt;와 &lt;code&gt;False&lt;/code&gt;는 &amp;ldquo;Positive인가?&amp;rdquo;를 직접 뜻하지 않는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;True&lt;/code&gt;: 모델의 예측이 실제 정답과 일치했다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;False&lt;/code&gt;: 모델의 예측이 실제 정답과 달랐다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Positive와 Negative는 무엇을 뜻할까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Positive&lt;/code&gt;와 &lt;code&gt;Negative&lt;/code&gt;는 모델이 어느 Class라고 판정했는지 또는 실제 어느 Class인지를 뜻한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Positive&lt;/code&gt;: 이번 문제에서 찾으려는 대상, 즉 숫자 5&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Negative&lt;/code&gt;: 찾으려는 대상이 아닌 것, 즉 숫자 5가 아닌 숫자&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이름을 뒤에서부터 읽으면 쉽다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;이름&lt;/th&gt;
&lt;th&gt;뒤의 단어&lt;/th&gt;
&lt;th&gt;앞의 단어&lt;/th&gt;
&lt;th&gt;숫자 5 예제&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;True Positive&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Positive라고 예측&lt;/td&gt;
&lt;td&gt;그 예측이 맞음&lt;/td&gt;
&lt;td&gt;실제 5를 5라고 예측&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;False Positive&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Positive라고 예측&lt;/td&gt;
&lt;td&gt;그 예측이 틀림&lt;/td&gt;
&lt;td&gt;다른 숫자를 5라고 오해&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;True Negative&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Negative라고 예측&lt;/td&gt;
&lt;td&gt;그 예측이 맞음&lt;/td&gt;
&lt;td&gt;5가 아닌 숫자를 정확히 제외&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;False Negative&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Negative라고 예측&lt;/td&gt;
&lt;td&gt;그 예측이 틀림&lt;/td&gt;
&lt;td&gt;실제 5를 놓침&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;가장 헷갈리는 FP와 FN&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;FP&lt;/code&gt;와 &lt;code&gt;FN&lt;/code&gt;은 &lt;b&gt;모델이 예측한 방향&lt;/b&gt;을 기준으로 이름이 붙는다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;FP: Positive라고 했는데 False였다.
    &amp;rarr; 실제로는 Negative

FN: Negative라고 했는데 False였다.
    &amp;rarr; 실제로는 Positive&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;질병 검사로 바꾸면 더 직관적이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;FP&lt;/code&gt;: 건강한 사람에게 양성이라고 알리는 &lt;code&gt;False Alarm&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FN&lt;/code&gt;: 아픈 사람에게 음성이라고 알려 질병을 놓치는 &lt;code&gt;Miss&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어느 오류가 더 심각한지는 문제에 따라 달라진다. 암 검진에서는 &lt;code&gt;FN&lt;/code&gt;을 줄이는 것이 매우 중요할 수 있고, 정상 메일을 스팸으로 차단하는 서비스에서는 &lt;code&gt;FP&lt;/code&gt;도 큰 문제다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 수업 결과를 직접 읽어 보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수업의 Validation Data에서 &lt;code&gt;SGDClassifier&lt;/code&gt;가 만든 &lt;code&gt;Confusion Matrix&lt;/code&gt;는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[[391,  0],
 [  6, 53]]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위치에 맞춰 이름을 붙이면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;TN = 391    FP = 0
FN =   6    TP = 53&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;수업 결과 [[391, 0], [6, 53]]의 의미&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;confusion-matrix-figure-02.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;650&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VniAq/dJMcaaGJAhW/070AesQGaw2I4m5JnP2Qi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VniAq/dJMcaaGJAhW/070AesQGaw2I4m5JnP2Qi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VniAq/dJMcaaGJAhW/070AesQGaw2I4m5JnP2Qi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVniAq%2FdJMcaaGJAhW%2F070AesQGaw2I4m5JnP2Qi0%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;1200&quot; height=&quot;650&quot; data-filename=&quot;confusion-matrix-figure-02.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;650&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 칸씩 문장으로 읽어 보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;TN = 391&lt;/code&gt;: 실제로 5가 아닌 이미지 391개를 모두 5가 아니라고 맞혔다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FP = 0&lt;/code&gt;: 5가 아닌 이미지를 5라고 잘못 예측한 경우는 없다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FN = 6&lt;/code&gt;: 실제 숫자 5인데도 5가 아니라고 예측한 이미지가 6개다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TP = 53&lt;/code&gt;: 실제 숫자 5를 5라고 정확히 예측한 이미지가 53개다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 모델은 숫자 5라고 예측할 때는 틀리지 않았지만, 실제 숫자 5 중 6개를 놓쳤다. 따라서 &lt;code&gt;Precision&lt;/code&gt;은 완벽하지만 &lt;code&gt;Recall&lt;/code&gt;은 완벽하지 않다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드로 확인하기&lt;/h3&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;from sklearn.metrics import confusion_matrix

cm = confusion_matrix(y_valid, y_pred)
print(cm)

tn, fp, fn, tp = cm.ravel()
print(&quot;TN:&quot;, tn)
print(&quot;FP:&quot;, fp)
print(&quot;FN:&quot;, fn)
print(&quot;TP:&quot;, tp)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ravel()&lt;/code&gt;은 2&amp;times;2 배열을 한 줄로 펼친다. &lt;code&gt;scikit-learn&lt;/code&gt;의 배열 순서가 &lt;code&gt;TN, FP, FN, TP&lt;/code&gt;이므로 위와 같이 받을 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의: 책이나 도구에 따라 행과 열의 방향이 반대로 표시되기도 한다. 숫자만 외우지 말고 그래프의 &lt;code&gt;Actual&lt;/code&gt;과 &lt;code&gt;Predicted&lt;/code&gt; 축을 먼저 확인해야 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. Confusion Matrix에서 평가 지표 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네 값만 알면 주요 &lt;code&gt;Classification Metric&lt;/code&gt;을 계산할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.1 Accuracy: 전체적으로 얼마나 맞혔는가?&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Accuracy = (TP + TN) / (TP + TN + FP + FN)

         = (53 + 391) / 450
         = 444 / 450
         &amp;asymp; 0.9867&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 전체 450개 중 444개를 맞혀 약 98.7%다. Class 비율이 비슷하고 오류 비용도 비슷할 때 이해하기 좋은 지표다. 그러나 &lt;code&gt;Class Imbalance&lt;/code&gt;가 크면 반드시 다른 지표와 함께 봐야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.2 Precision: Positive라고 한 예측을 얼마나 믿을 수 있는가?&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Precision = TP / (TP + FP)

          = 53 / (53 + 0)
          = 1.0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분모는 &lt;b&gt;모델이 Positive라고 예측한 전체&lt;/b&gt;다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;모델이 &amp;ldquo;5다&amp;rdquo;라고 말한 것 중 진짜 5는 몇 개인가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수업 모델의 &lt;code&gt;Precision&lt;/code&gt;은 100%다. 숫자 5라고 예측한 53개가 모두 실제 5였다는 뜻이다. &lt;code&gt;FP&lt;/code&gt;를 줄이고 싶을 때 중요한 지표다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.3 Recall: 실제 Positive를 얼마나 찾아냈는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Recall&lt;/code&gt;은 &lt;code&gt;Sensitivity&lt;/code&gt; 또는 &lt;code&gt;True Positive Rate&lt;/code&gt;라고도 한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Recall = TP / (TP + FN)

       = 53 / (53 + 6)
       = 53 / 59
       &amp;asymp; 0.8983&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분모는 &lt;b&gt;실제 Positive 전체&lt;/b&gt;다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;실제로 5인 것 중 모델이 찾아낸 것은 몇 개인가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 숫자 5는 59개였고 모델은 53개를 찾아 약 89.8%의 &lt;code&gt;Recall&lt;/code&gt;을 얻었다. 나머지 6개는 &lt;code&gt;FN&lt;/code&gt;이다. 놓치면 위험한 질병 탐지, 사기 거래 탐지 같은 문제에서 특히 중요하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.4 Specificity: 실제 Negative를 얼마나 잘 제외했는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Specificity&lt;/code&gt;는 &lt;code&gt;True Negative Rate&lt;/code&gt;라고도 한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Specificity = TN / (TN + FP)

            = 391 / (391 + 0)
            = 1.0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분모는 &lt;b&gt;실제 Negative 전체&lt;/b&gt;다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;실제로 5가 아닌 것 중 모델이 정확히 제외한 것은 몇 개인가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 모델은 5가 아닌 숫자 391개를 모두 정확히 제외했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.5 F1 Score: Precision과 Recall의 균형&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;F1 Score = 2 &amp;times; (Precision &amp;times; Recall) / (Precision + Recall)

         = 2 &amp;times; (1.0 &amp;times; 0.8983) / (1.0 + 0.8983)
         &amp;asymp; 0.9464&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;F1 Score&lt;/code&gt;는 두 값의 단순 평균이 아니라 &lt;code&gt;Harmonic Mean&lt;/code&gt;을 사용한다. 둘 중 하나만 매우 낮으면 점수가 크게 떨어지므로 &lt;code&gt;Precision&lt;/code&gt;과 &lt;code&gt;Recall&lt;/code&gt;을 모두 중요하게 볼 때 유용하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.6 FPR과 FNR: 오류의 비율&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;FPR = FP / (TN + FP) = 0 / 391 = 0
FNR = FN / (TP + FN) = 6 / 59 &amp;asymp; 0.1017&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;False Positive Rate&lt;/code&gt;: 실제 Negative 중 Positive로 잘못 알린 비율&lt;/li&gt;
&lt;li&gt;&lt;code&gt;False Negative Rate&lt;/code&gt;: 실제 Positive 중 Negative로 놓친 비율&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Recall + FNR = 1&lt;/code&gt;, &lt;code&gt;Specificity + FPR = 1&lt;/code&gt; 관계가 성립한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;지표를 질문으로 외우기&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;분모가 되는 집단&lt;/th&gt;
&lt;th&gt;답하는 질문&lt;/th&gt;
&lt;th&gt;줄이고 싶은 오류&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Accuracy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;전체 Sample&lt;/td&gt;
&lt;td&gt;전체적으로 몇 개를 맞혔나?&lt;/td&gt;
&lt;td&gt;전체 오류&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Precision&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Predicted Positive&lt;/td&gt;
&lt;td&gt;Positive 예측을 얼마나 믿을 수 있나?&lt;/td&gt;
&lt;td&gt;&lt;code&gt;FP&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Recall&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Actual Positive&lt;/td&gt;
&lt;td&gt;실제 Positive를 얼마나 찾았나?&lt;/td&gt;
&lt;td&gt;&lt;code&gt;FN&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Specificity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Actual Negative&lt;/td&gt;
&lt;td&gt;실제 Negative를 얼마나 제외했나?&lt;/td&gt;
&lt;td&gt;&lt;code&gt;FP&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;F1 Score&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Precision과 Recall&lt;/td&gt;
&lt;td&gt;두 지표가 균형 잡혔나?&lt;/td&gt;
&lt;td&gt;&lt;code&gt;FP&lt;/code&gt;, &lt;code&gt;FN&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. Normalized Confusion Matrix는 왜 볼까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수업 코드의 &lt;code&gt;normalize=&quot;true&quot;&lt;/code&gt;는 각 &lt;code&gt;Actual Class&lt;/code&gt;의 개수로 행을 나눈다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;confusion_matrix(y_train, y_train_pred, normalize=&quot;true&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수업의 Training Data 결과는 다음과 같았다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[[0.9935, 0.0065],
 [0.0569, 0.9431]]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 행의 합은 1이 된다.&lt;/p&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;첫 번째 행: 실제 Negative가 어떻게 예측되었는가?
두 번째 행: 실제 Positive가 어떻게 예측되었는가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이 표는 다음처럼 읽는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 Negative의 약 99.35%를 Negative로 맞혔다.&lt;/li&gt;
&lt;li&gt;실제 Negative의 약 0.65%를 Positive로 잘못 예측했다.&lt;/li&gt;
&lt;li&gt;실제 Positive의 약 5.69%를 Negative로 놓쳤다.&lt;/li&gt;
&lt;li&gt;실제 Positive의 약 94.31%를 Positive로 맞혔다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Raw Count&lt;/code&gt;와 &lt;code&gt;Normalized Value&lt;/code&gt; 중 하나가 더 정확한 것은 아니다. 용도가 다르다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Raw Count&lt;/code&gt;: 실제 오류가 몇 건 발생했는지 보여 준다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Normalized Confusion Matrix&lt;/code&gt;: Class 크기가 다를 때 Class별 비율을 비교하기 쉽다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;normalize&lt;/code&gt; 옵션의 의미도 알아 두면 좋다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;옵션&lt;/th&gt;
&lt;th&gt;나누는 기준&lt;/th&gt;
&lt;th&gt;합이 1이 되는 방향&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;None&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;나누지 않음&lt;/td&gt;
&lt;td&gt;해당 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&quot;true&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Actual Class&lt;/td&gt;
&lt;td&gt;각 행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&quot;pred&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Predicted Class&lt;/td&gt;
&lt;td&gt;각 열&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&quot;all&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;전체 Sample&lt;/td&gt;
&lt;td&gt;표 전체&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. Decision Threshold는 무엇인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분류 모델이 처음부터 바로 0 또는 1만 만드는 것은 아니다. 먼저 해당 Sample이 Positive 쪽에 얼마나 가까운지를 나타내는 &lt;code&gt;Score&lt;/code&gt;나 &lt;code&gt;Probability&lt;/code&gt;를 만들고, 기준점을 사용해 최종 Class로 바꾼다. 이 기준점이 &lt;code&gt;Decision Threshold&lt;/code&gt;다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Score &amp;gt;= Threshold  &amp;rarr; Positive 예측
Score &amp;lt;  Threshold  &amp;rarr; Negative 예측&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;SGDClassifier&lt;/code&gt;에서는 &lt;code&gt;decision_function()&lt;/code&gt;으로 &lt;code&gt;Decision Score&lt;/code&gt;를 얻을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;y_score = model.decision_function(x_valid)

threshold = 0
y_pred = (y_score &amp;gt;= threshold).astype(int)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;Decision Score&lt;/code&gt;는 반드시 확률은 아니다. 특히 수업에서 기본 설정의 &lt;code&gt;SGDClassifier&lt;/code&gt;가 반환하는 값은 결정 경계로부터의 방향과 정도를 나타내는 점수다. 0.8이라고 해서 곧바로 80% 확률이라는 뜻은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;DecisionTreeClassifier.predict_proba()&lt;/code&gt;는 0과 1 사이의 추정 확률을 반환한다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;y_probability = tree_model.predict_proba(x_valid)[:, 1]

threshold = 0.5
y_pred = (y_probability &amp;gt;= threshold).astype(int)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Threshold를 높이면&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Positive 판정을 더 엄격하게 한다. Positive라고 예측하는 수가 줄어든다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;FP&lt;/code&gt;는 줄어들 가능성이 크다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Precision&lt;/code&gt;은 대체로 높아진다.&lt;/li&gt;
&lt;li&gt;실제 Positive도 놓칠 수 있어 &lt;code&gt;FN&lt;/code&gt;이 늘어난다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Recall&lt;/code&gt;은 대체로 낮아진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Threshold를 낮추면&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Positive 판정을 더 너그럽게 한다. Positive라고 예측하는 수가 늘어난다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 Positive를 더 많이 찾아 &lt;code&gt;Recall&lt;/code&gt;이 대체로 높아진다.&lt;/li&gt;
&lt;li&gt;Negative까지 Positive로 판단해 &lt;code&gt;FP&lt;/code&gt;가 늘 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Precision&lt;/code&gt;은 대체로 낮아진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Threshold를 기준으로 Predicted Negative와 Predicted Positive가 나뉘는 과정&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;confusion-matrix-figure-04.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;650&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ICnaB/dJMcajwRhIh/DKJtL1E95m50oTBRrzx51K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ICnaB/dJMcajwRhIh/DKJtL1E95m50oTBRrzx51K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ICnaB/dJMcajwRhIh/DKJtL1E95m50oTBRrzx51K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FICnaB%2FdJMcajwRhIh%2FDKJtL1E95m50oTBRrzx51K%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;1200&quot; height=&quot;650&quot; data-filename=&quot;confusion-matrix-figure-04.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;650&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수업에서 극단적인 Threshold를 실험한 이유도 이 관계를 보기 위해서다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Threshold를 매우 높임
&amp;rarr; 거의 아무것도 Positive라고 하지 않음
&amp;rarr; TP도 FP도 0이 될 수 있음
&amp;rarr; Recall = 0

Threshold를 매우 낮춤
&amp;rarr; 거의 모든 것을 Positive라고 함
&amp;rarr; 실제 Positive는 전부 찾을 수 있음
&amp;rarr; Recall = 1
&amp;rarr; 하지만 FP가 많아져 Precision이 낮아짐&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수업 결과에서 &lt;code&gt;Threshold = -30000&lt;/code&gt;일 때 &lt;code&gt;Recall = 1&lt;/code&gt;이었지만 &lt;code&gt;Precision &amp;asymp; 0.1311&lt;/code&gt;이었다. 숫자 5를 하나도 놓치지는 않았지만, 5가 아닌 숫자까지 많이 5라고 예측했다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 &lt;code&gt;Threshold = 30000&lt;/code&gt;에서는 Positive라고 예측한 Sample이 하나도 없어 &lt;code&gt;Recall = 0&lt;/code&gt;이었다. 이 경우 &lt;code&gt;TP + FP = 0&lt;/code&gt;이므로 &lt;code&gt;Precision&lt;/code&gt;의 분모가 0이다. 그래서 &lt;code&gt;UndefinedMetricWarning&lt;/code&gt;이 발생할 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Threshold는 무조건 0.5를 쓰는 고정 규칙이 아니다. 서비스 목적과 &lt;code&gt;FP&lt;/code&gt;, &lt;code&gt;FN&lt;/code&gt;의 비용을 고려하여 Validation Data에서 선택해야 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. Precision과 Recall 중 무엇을 선택해야 할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평가 지표는 모델만 보고 정하는 것이 아니라 &lt;b&gt;틀렸을 때 발생하는 비용&lt;/b&gt;을 보고 정한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Recall을 더 중요하게 보는 경우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Positive를 놓치는 &lt;code&gt;FN&lt;/code&gt;이 큰 피해를 만드는 문제다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;암 환자 Screening&lt;/li&gt;
&lt;li&gt;화재 감지&lt;/li&gt;
&lt;li&gt;Fraud Detection의 초기 Screening&lt;/li&gt;
&lt;li&gt;제조 공정의 불량품 탐지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Threshold를 낮춰 의심 사례를 넓게 잡고, 이후 사람이 다시 검사하는 방식이 가능하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Precision을 더 중요하게 보는 경우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Positive로 잘못 판단하는 &lt;code&gt;FP&lt;/code&gt;의 비용이 큰 문제다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정상 메일을 Spam으로 차단&lt;/li&gt;
&lt;li&gt;무고한 사용자의 계정을 정지&lt;/li&gt;
&lt;li&gt;비용이 매우 큰 후속 검사를 자동 실행&lt;/li&gt;
&lt;li&gt;추천 알림을 너무 자주 발송&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;둘 다 중요하다면&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;F1 Score&lt;/code&gt;, &lt;code&gt;Precision-Recall Curve&lt;/code&gt;, 업무 비용을 함께 확인한다. 단, &lt;code&gt;F1 Score&lt;/code&gt;는 &lt;code&gt;TN&lt;/code&gt;을 직접 사용하지 않기 때문에 모든 문제에서 만능 지표는 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. Type I Error와 Type II Error는 같은 말일까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수업에서는 &lt;code&gt;FP&lt;/code&gt;를 &lt;code&gt;Type I Error&lt;/code&gt;, &lt;code&gt;FN&lt;/code&gt;을 &lt;code&gt;Type II Error&lt;/code&gt;에 대응해 설명했다. 다음과 같이 가설을 정의했다면 이런 대응이 가능하다.&lt;/p&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;H0: Negative다. 즉 숫자 5가 아니다.
H1: Positive다. 즉 숫자 5다.

Positive 예측 = H0를 기각
Negative 예측 = H0를 기각하지 않음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 다음처럼 연결된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Type I Error&lt;/code&gt;: H0가 참인데 기각 &amp;rarr; 실제 Negative를 Positive로 판단 &amp;rarr; &lt;code&gt;FP&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Type II Error&lt;/code&gt;: H1이 참인데 H0를 기각하지 못함 &amp;rarr; 실제 Positive를 Negative로 판단 &amp;rarr; &lt;code&gt;FN&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 두 분야의 개념이 언제나 자동으로 같은 것은 아니다. &lt;code&gt;Hypothesis Testing&lt;/code&gt;에서는 H0와 H1을 어떻게 정했는지가 중요하고, &lt;code&gt;Classification&lt;/code&gt;에서는 어느 Class를 Positive로 정했는지가 중요하다. 정의를 반대로 잡으면 대응도 달라질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음처럼 기억하는 편이 안전하다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;FP&lt;/code&gt;와 &lt;code&gt;FN&lt;/code&gt;은 Confusion Matrix에서 직접 정의하고, &lt;code&gt;Type I/II Error&lt;/code&gt;와의 연결은 H0와 Positive Class를 확인한 뒤 사용한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10. 수업 코드의 전체 흐름&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;10.1 데이터 준비와 분할&lt;/h3&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split

digits = load_digits()

x = digits.data
y = (digits.target == 5).astype(int)

x_train, x_valid, y_train, y_valid = train_test_split(
    x,
    y,
    random_state=42,
)

print(x_train.shape)  # (1347, 64)
print(x_valid.shape)  # (450, 64)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 수업 결과를 그대로 재현하는 형태다. &lt;code&gt;Class Imbalance&lt;/code&gt;가 있는 실전 코드에서는 다음처럼 &lt;code&gt;stratify=y&lt;/code&gt;를 추가하면 Training과 Validation에 원래 Class 비율이 비슷하게 유지된다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;x_train, x_valid, y_train, y_valid = train_test_split(
    x,
    y,
    test_size=0.25,
    random_state=42,
    stratify=y,
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, &lt;code&gt;stratify&lt;/code&gt;를 추가하면 분할된 Sample이 달라지므로 수업에서 나온 숫자와 결과가 조금 달라질 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;10.2 Baseline과 학습 모델 비교&lt;/h3&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;from sklearn.dummy import DummyClassifier
from sklearn.linear_model import SGDClassifier

dummy_model = DummyClassifier(
    strategy=&quot;most_frequent&quot;,
    random_state=42,
)
dummy_model.fit(x_train, y_train)

model = SGDClassifier(random_state=42)
model.fit(x_train, y_train)

dummy_pred = dummy_model.predict(x_valid)
y_pred = model.predict(x_valid)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;10.3 Confusion Matrix와 주요 Metric 계산&lt;/h3&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;from sklearn.metrics import (
    confusion_matrix,
    accuracy_score,
    precision_score,
    recall_score,
    f1_score,
    classification_report,
)

print(confusion_matrix(y_valid, y_pred))
print(&quot;Accuracy :&quot;, accuracy_score(y_valid, y_pred))
print(&quot;Precision:&quot;, precision_score(y_valid, y_pred))
print(&quot;Recall   :&quot;, recall_score(y_valid, y_pred))
print(&quot;F1 Score :&quot;, f1_score(y_valid, y_pred))

print(classification_report(y_valid, y_pred))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수업 결과는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;Accuracy  = 0.9867
Precision = 1.0000
Recall    = 0.8983
F1 Score  = 0.9464&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;10.4 Confusion Matrix 시각화&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import matplotlib.pyplot as plt
from sklearn.metrics import ConfusionMatrixDisplay

fig, axes = plt.subplots(1, 2, figsize=(10, 4))

ConfusionMatrixDisplay.from_predictions(
    y_valid,
    y_pred,
    display_labels=[&quot;Not 5&quot;, &quot;5&quot;],
    cmap=&quot;Blues&quot;,
    ax=axes[0],
)
axes[0].set_title(&quot;Raw Count&quot;)

ConfusionMatrixDisplay.from_predictions(
    y_valid,
    y_pred,
    display_labels=[&quot;Not 5&quot;, &quot;5&quot;],
    normalize=&quot;true&quot;,
    values_format=&quot;.2f&quot;,
    cmap=&quot;Blues&quot;,
    ax=axes[1],
)
axes[1].set_title(&quot;Normalized by Actual Class&quot;)

plt.tight_layout()
plt.show()&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11. cross_val_predict는 왜 사용했을까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수업에서는 Training Data에 대해 다음 코드를 사용했다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;from sklearn.model_selection import cross_val_predict

y_train_pred = cross_val_predict(
    model,
    x_train,
    y_train,
    cv=10,
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Training Data로 학습한 모델을 같은 Training Data에 바로 예측시키면 이미 본 문제를 다시 풀게 된다. 성능이 실제보다 좋아 보일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;cross_val_predict&lt;/code&gt;는 Training Data를 10개 &lt;code&gt;Fold&lt;/code&gt;로 나누고 다음 과정을 반복한다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;9개 Fold로 Training
        &amp;darr;
남은 1개 Fold를 Prediction
        &amp;darr;
모든 Fold가 한 번씩 Prediction 대상이 될 때까지 반복&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 각 Sample의 예측값은 그 Sample을 학습할 때 보지 않은 모델이 만든 &lt;code&gt;Out-of-Fold Prediction&lt;/code&gt;이다. Training Data를 활용하면서도 단순한 Training Score보다 정직한 평가를 얻을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 이것은 최종 Validation 평가와 역할이 다르다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;cross_val_predict&lt;/code&gt;: Training Data 안에서 분석용 &lt;code&gt;Out-of-Fold Prediction&lt;/code&gt; 생성&lt;/li&gt;
&lt;li&gt;&lt;code&gt;model.predict(x_valid)&lt;/code&gt;: 따로 보관한 Validation Data에서 최종 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;12. 자주 하는 실수&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실수 1: Positive를 무조건 좋은 것으로 생각하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Positive&lt;/code&gt;는 좋은 결과가 아니라 &lt;b&gt;찾기로 정한 Class&lt;/b&gt;다. 질병, 사기, 불량품처럼 좋지 않은 대상도 Positive가 될 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실수 2: 행과 열의 방향을 확인하지 않기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;scikit-learn&lt;/code&gt;은 행이 &lt;code&gt;Actual&lt;/code&gt;, 열이 &lt;code&gt;Predicted&lt;/code&gt;다. 다른 자료에서는 반대로 그릴 수 있으므로 항상 축 이름을 확인한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실수 3: Precision과 Recall의 분모를 바꾸어 외우기&lt;/h3&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Precision &amp;rarr; Predicted Positive를 분모로 사용
Recall    &amp;rarr; Actual Positive를 분모로 사용&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영어 첫 글자 &lt;code&gt;P&lt;/code&gt;를 연결하면 조금 더 기억하기 쉽다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실수 4: Accuracy 하나만 보고 모델을 선택하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dummy Model도 86.9%를 얻었지만 숫자 5를 한 개도 찾지 못했다. &lt;code&gt;Class Distribution&lt;/code&gt;, &lt;code&gt;Confusion Matrix&lt;/code&gt;, 목적에 맞는 Metric을 함께 확인해야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실수 5: Decision Score를 확률로 해석하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;decision_function()&lt;/code&gt;의 값은 일반적으로 확률이 아니다. 확률이 필요하다면 &lt;code&gt;predict_proba()&lt;/code&gt;를 지원하는 모델을 사용하거나 &lt;code&gt;Probability Calibration&lt;/code&gt;을 고려해야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실수 6: Test Data로 Threshold를 조정하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Threshold와 Hyperparameter는 &lt;code&gt;Validation Data&lt;/code&gt;로 선택한다. &lt;code&gt;Test Data&lt;/code&gt;는 모든 선택이 끝난 후 일반화 성능을 한 번 확인하는 용도로 남겨 두는 것이 좋다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;13. 한 번에 정리하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Confusion Matrix&lt;/code&gt;의 목적은 단순하다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델이 얼마나 맞혔는지를 넘어, 어떤 종류의 정답을 맞히고 어떤 종류의 오류를 내는지 확인한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 숫자 5 분류 결과는 다음과 같이 요약할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Confusion Matrix = [[391, 0], [6, 53]]

5가 아닌 숫자 391개 &amp;rarr; 모두 정확히 제외
실제 숫자 5 59개     &amp;rarr; 53개 탐지, 6개 놓침

Accuracy  = 98.67%
Precision = 100.00%
Recall    = 89.83%
Specificity = 100.00%
F1 Score  = 94.64%&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 중요한 해석은 &amp;ldquo;Accuracy가 98.67%다&amp;rdquo;에서 끝나지 않는다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;숫자 5라고 예측한 결과는 모두 맞았다.
하지만 실제 숫자 5의 약 10.17%는 놓쳤다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델을 실제 서비스에 사용할 수 있는지는 이 여섯 번의 &lt;code&gt;FN&lt;/code&gt;을 허용할 수 있는지에 달려 있다. 허용할 수 없다면 Threshold를 낮춰 &lt;code&gt;Recall&lt;/code&gt;을 높일 수 있지만, 그 대가로 &lt;code&gt;FP&lt;/code&gt;가 증가하고 &lt;code&gt;Precision&lt;/code&gt;이 낮아질 수 있다. 결국 좋은 평가란 가장 높은 숫자를 찾는 일이 아니라, &lt;b&gt;문제의 비용에 맞는 Metric과 Threshold를 선택하는 일&lt;/b&gt;이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;핵심 용어 요약&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;용어&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Binary Classification&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;두 Class 중 하나를 예측하는 문제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Positive Class&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;모델이 찾아내도록 정한 대상&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Negative Class&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Positive가 아닌 대상&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Confusion Matrix&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Actual과 Predicted의 조합을 센 표&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Positive를 Positive로 맞힘&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Negative를 Negative로 맞힘&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Negative를 Positive로 잘못 예측&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Positive를 Negative로 잘못 예측&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Precision&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Positive 예측 중 실제 Positive의 비율&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Recall&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;실제 Positive 중 찾아낸 비율&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Specificity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;실제 Negative 중 정확히 제외한 비율&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;F1 Score&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Precision과 Recall의 Harmonic Mean&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Decision Threshold&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Score를 Class로 바꾸는 기준점&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Class Imbalance&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Class별 Sample 수가 크게 다른 상태&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Baseline Model&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;학습 모델의 성능을 비교하기 위한 단순한 기준 모델&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description>
      <category>ML</category>
      <author>rem1004</author>
      <guid isPermaLink="true">https://rem1004.tistory.com/52</guid>
      <comments>https://rem1004.tistory.com/52#entry52comment</comments>
      <pubDate>Fri, 4 Sep 2026 17:55:41 +0900</pubDate>
    </item>
    <item>
      <title># 머신러닝 1편: 선형회귀, SSE&amp;middot;SST&amp;middot;SSR&amp;middot;R&amp;sup2;와 경사하강법</title>
      <link>https://rem1004.tistory.com/51</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;머신러닝에서 가장 먼저 배우는 모델 중 하나가 선형회귀입니다. 선형회귀는 단순히 직선을 하나 긋는 방법처럼 보이지만, 그 안에는 다음과 같은 중요한 개념이 모두 들어 있습니다.&lt;/p&gt;
&lt;pre class=&quot;autohotkey&quot;&gt;&lt;code&gt;예측값과 실제값의 차이
잔차와 제곱오차
SSE, SST, SSR
R&amp;sup2;와 기준 모델
최소제곱법
목적함수와 gradient
경사하강법&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 &amp;ldquo;왜 평균이 회귀의 기준이 되는가?&amp;rdquo;라는 질문에서 출발해 선형회귀가 오차를 어떻게 줄이는지까지 한 번에 연결해 보겠습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 선형회귀란 무엇인가?&lt;/h2&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;pre class=&quot;ini&quot;&gt;&lt;code&gt;y_hat = w x + b&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 각각의 의미는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;x     = 입력 변수
y     = 실제 정답
y_hat = 모델의 예측값
w     = 기울기
b     = 절편&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기울기 w는 x가 1만큼 증가할 때 예측값이 얼마나 변하는지를 나타냅니다. 절편 b는 x가 0일 때의 예측값입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;y_hat = 2x + 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이라면 x가 1 증가할 때 예측값은 2만큼 증가합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;x = 0 -&amp;gt; y_hat = 1
x = 1 -&amp;gt; y_hat = 3
x = 2 -&amp;gt; y_hat = 5&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선형회귀의 목표는 데이터에 가장 잘 맞는 w와 b를 찾는 것입니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;좋은 w, b 찾기
-&amp;gt; 예측값 y_hat과 실제값 y의 차이를 작게 만들기&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;01-regression-line-and-residuals.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crLe8w/dJMcaiYPDon/iOAAKf8ZqRu3VG1vKJBwvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crLe8w/dJMcaiYPDon/iOAAKf8ZqRu3VG1vKJBwvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crLe8w/dJMcaiYPDon/iOAAKf8ZqRu3VG1vKJBwvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrLe8w%2FdJMcaiYPDon%2FiOAAKf8ZqRu3VG1vKJBwvk%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;1200&quot; height=&quot;700&quot; data-filename=&quot;01-regression-line-and-residuals.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 예측 오차와 잔차&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 데이터의 예측 오차를 잔차, residual이라고 합니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;e_i = y_i - y_hat_i&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 실제값이 10이고 예측값이 8이라면&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;e = 10 - 8 = 2&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;예측값이 실제값보다 크면 잔차는 음수가 됩니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;실제값 y = 10
예측값 y_hat = 12

잔차 e = 10 - 12 = -2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잔차의 부호는 모델이 어느 방향으로 틀렸는지를 알려 줍니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;e_i &amp;gt; 0:
모델이 실제값보다 작게 예측

e_i &amp;lt; 0:
모델이 실제값보다 크게 예측&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 잔차를 그냥 모두 더하면 문제가 생깁니다. 양의 잔차와 음의 잔차가 서로 상쇄될 수 있기 때문입니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;잔차 = [2, -2]

잔차의 합 = 2 + (-2) = 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오차가 분명히 존재하지만 합만 보면 완벽하게 맞춘 것처럼 보입니다. 그래서 선형회귀에서는 잔차를 제곱한 뒤 더합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. SSE: 제곱오차의 합&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSE는 Sum of Squared Errors의 약자입니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;SSE = sum_i (y_i - y_hat_i)^2&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;데이터가 세 개라면&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;SSE
= (y_1 - y_hat_1)^2
+ (y_2 - y_hat_2)^2
+ (y_3 - y_hat_3)^2&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;SSE가 작을수록 모델의 예측이 실제값에 가깝습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;간단한 예시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제값이&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;y = [10, 20, 30]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이고 모델이&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;y_hat = [12, 20, 28]&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;각 잔차는&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;e = y - y_hat
  = [10-12, 20-20, 30-28]
  = [-2, 0, 2]&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;따라서 SSE는&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;SSE
= (-2)^2 + 0^2 + 2^2
= 4 + 0 + 4
= 8&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;SSE는 회귀모델이 훈련 데이터에서 얼마나 틀렸는지를 나타내는 가장 기본적인 목적함수입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 왜 오차를 제곱할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제곱오차를 사용하는 이유는 여러 가지가 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 양수와 음수의 상쇄 방지&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;잔차 합:
2 + (-2) = 0

제곱잔차 합:
2^2 + (-2)^2 = 8&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 큰 오차를 더 강하게 벌점&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;오차 1 -&amp;gt; 제곱오차 1
오차 2 -&amp;gt; 제곱오차 4
오차 5 -&amp;gt; 제곱오차 25&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큰 오차가 있는 데이터에 더 큰 패널티를 줍니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 미분하기 쉬움&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;절댓값은 0에서 미분이 매끄럽지 않지만, 제곱함수는 모든 지점에서 미분할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;(y - y_hat)^2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;은 경사하강법으로 최적화하기 좋습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 통계적 의미&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오차가 평균 0인 Gaussian noise를 따른다고 가정하면, 제곱오차를 최소화하는 것은 likelihood를 최대화하는 것과 연결됩니다.&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;Gaussian noise 가정
-&amp;gt; 최대우도추정
-&amp;gt; 제곱오차 최소화&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 제곱오차는 이상치에 민감합니다. 아주 큰 오차 하나가 전체 SSE를 크게 키울 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 아무 정보가 없을 때의 기준 모델&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 중요한 질문을 생각해 보겠습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;x 정보를 전혀 사용하지 않고, 모든 데이터에 하나의 숫자만 예측해야 한다면 어떤 숫자를 선택해야 할까?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델이 모든 데이터에 같은 값 c를 예측한다고 합시다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;y_hat_i = c&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 목적함수는&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;SSE(c) = sum_i (y_i - c)^2&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;실제값이&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;y = [10, 20, 30]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이라면&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;SSE(c)
= (10-c)^2 + (20-c)^2 + (30-c)^2&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;우리는 이 SSE(c)를 가장 작게 만드는 c를 찾고 싶습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 평균이 기준인 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSE(c)를 c에 대해 미분합니다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;dSSE(c)/dc
= -2(10-c) - 2(20-c) - 2(30-c)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;dSSE(c)/dc
= -2(60 - 3c)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최솟값에서는 미분값이 0이므로&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;-2(60 - 3c) = 0
60 - 3c = 0
c = 20&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;그런데 실제값의 평균은&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;(10 + 20 + 30) / 3 = 20&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;따라서 제곱오차를 기준으로 모든 데이터에 하나의 숫자만 예측해야 한다면, 가장 좋은 숫자는 평균입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로도 같습니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;SSE(c) = sum_(i=1)^n (y_i - c)^2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 미분하면&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;dSSE(c)/dc
= -2 sum_i (y_i - c)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최솟값에서 0으로 놓으면&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;sum_i (y_i - c) = 0
sum_i y_i - n c = 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;c = (1/n) sum_i y_i
  = y_bar&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;즉, 평균은 다음과 같은 의미를 가집니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;x 정보를 전혀 사용하지 않고 하나의 숫자만 예측할 때, 제곱오차를 가장 작게 만드는 기준 예측값&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;02-sse-c-mean-minimum.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRy8wP/dJMcahsixFR/ezs5I6YCcnUHHUFSPqbnwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRy8wP/dJMcahsixFR/ezs5I6YCcnUHHUFSPqbnwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRy8wP/dJMcahsixFR/ezs5I6YCcnUHHUFSPqbnwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRy8wP%2FdJMcahsixFR%2Fezs5I6YCcnUHHUFSPqbnwk%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;1200&quot; height=&quot;700&quot; data-filename=&quot;02-sse-c-mean-minimum.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 결과는 선형회귀의 중요한 기준이 됩니다.&lt;/p&gt;
&lt;pre class=&quot;gml&quot;&gt;&lt;code&gt;기준 모델:
모든 데이터에 평균 y_bar 예측

비교 모델:
x를 사용해 y_hat = w x + b 예측&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회귀모델은 최소한 평균만 예측하는 기준 모델보다 잘해야 의미가 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 평균은 모든 오차 기준의 최선은 아니다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평균이 항상 기준이 되는 것은 아닙니다. 어떤 오차함수를 사용하느냐에 따라 최적의 대표값이 달라집니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;제곱오차&lt;/h3&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;sum_i (y_i - c)^2
-&amp;gt; 평균&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;절대오차&lt;/h3&gt;
&lt;pre class=&quot;gherkin&quot;&gt;&lt;code&gt;sum_i |y_i - c|
-&amp;gt; 중앙값&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;예를 들어&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;y = [1, 2, 100]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일 때 평균은 약 34.33이지만 중앙값은 2입니다. 이상치 100의 영향을 덜 받고 싶다면 절대오차와 중앙값이 더 적합할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;제곱오차 최소화 -&amp;gt; 평균
절대오차 최소화 -&amp;gt; 중앙값&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선형회귀와 R&amp;sup2;에서 평균이 등장하는 이유는 제곱오차를 기준으로 삼기 때문입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. SST: 전체 변동량&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 회귀모델이 설명한 변동과 설명하지 못한 변동을 나누어 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제값 y_i가 평균 y_bar에서 얼마나 떨어져 있는지를 측정하는 값이 전체제곱합, SST입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SST는 Total Sum of Squares의 약자입니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;SST = sum_i (y_i - y_bar)^2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;y = [10, 20, 30]
y_bar = 20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이면&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;SST
= (10-20)^2 + (20-20)^2 + (30-20)^2
= 100 + 0 + 100
= 200&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;SST는 x를 사용하기 전, y 데이터 자체가 가진 전체 흩어짐의 크기입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;분산과 SST&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;표본분산은&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;s^2 = 1/(n-1) sum_i (y_i - y_bar)^2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이므로&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;SST = (n-1) s^2&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;따라서 SST는 분산의 분자 부분이라고 생각할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;gml&quot;&gt;&lt;code&gt;SST가 큼:
y 값들이 평균에서 많이 흩어져 있음

SST가 작음:
y 값들이 평균 주변에 모여 있음&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. 회귀모델이 설명하는 변동&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회귀모델의 예측값을&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;y_hat_i = w x_i + b&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;실제값 y_i가 평균에서 떨어진 정도는 두 부분으로 나눌 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;y_i - y_bar
= (y_hat_i - y_bar) + (y_i - y_hat_i)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 항의 의미는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;y_i - y_bar:
실제값이 평균에서 떨어진 전체 변화

y_hat_i - y_bar:
회귀모델이 평균에서 움직여 준 변화

y_i - y_hat_i:
회귀모델이 설명하지 못하고 남은 잔차&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그림으로 보면 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;평균 y_bar
    |
    |  모델이 설명한 변동
    |
예측값 y_hat
    |
    |  모델이 설명하지 못한 잔차
    |
실제값 y&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델이 모든 예측을 평균으로만 하면&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;y_hat_i = y_bar&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이므로 설명한 변동은 0입니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;y_hat_i - y_bar = 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 회귀선이 x에 따른 y의 패턴을 잘 잡으면 예측값이 평균에서 의미 있게 움직입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10. SSR: 모델이 설명한 변동&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSR은 Regression Sum of Squares 또는 Explained Sum of Squares라고 부릅니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;SSR = sum_i (y_hat_i - y_bar)^2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSR은 회귀모델의 예측값이 평균 예측에서 얼마나 벗어났는지를 측정합니다.&lt;/p&gt;
&lt;pre class=&quot;gml&quot;&gt;&lt;code&gt;SSR가 큼:
모델이 y의 변화를 많이 설명

SSR가 작음:
모델이 평균 예측과 크게 다르지 않음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의할 점은 예측값이 평균에서 멀다고 무조건 좋은 것은 아니라는 점입니다. 예측값이 실제 패턴과 함께 움직이고 잔차도 작아야 좋은 모델입니다. 그래서 SSR과 SSE를 함께 봐야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11. SSE: 모델이 설명하지 못한 변동&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 정의한 SSE는 다시 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;SSE = sum_i (y_i - y_hat_i)^2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 의미를 &amp;ldquo;모델이 설명하지 못한 변동&amp;rdquo;으로 볼 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;SSE가 작음:
실제값과 예측값이 가까움

SSE가 큼:
회귀선에서 실제 데이터가 많이 벗어남&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSE는 훈련 과정에서 직접 최소화하는 값이고, SSR은 모델이 평균 기준에서 얼마나 설명력을 만들어 냈는지 보여 주는 값입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;12. SST = SSR + SSE&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 식에서 출발합니다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;y_i - y_bar
= (y_hat_i - y_bar) + (y_i - y_hat_i)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;양변을 제곱하면&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;(y_i - y_bar)^2
= (y_hat_i - y_bar)^2
+ (y_i - y_hat_i)^2
+ 2(y_hat_i - y_bar)(y_i - y_hat_i)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 데이터에 대해 더하면&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;sum_i (y_i - y_bar)^2
= sum_i (y_hat_i - y_bar)^2
+ sum_i (y_i - y_hat_i)^2
+ 2 sum_i (y_hat_i - y_bar)(y_i - y_hat_i)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최소제곱법으로 절편 b를 포함한 회귀선을 구하면 마지막 교차항이 0이 됩니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;sum_i (y_hat_i - y_bar)(y_i - y_hat_i) = 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;SST = SSR + SSE&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;각 항을 다시 정리하면&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;SST = sum_i (y_i - y_bar)^2
    = y의 전체 변동량

SSR = sum_i (y_hat_i - y_bar)^2
    = 모델이 설명한 변동량

SSE = sum_i (y_i - y_hat_i)^2
    = 모델이 설명하지 못한 변동량&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;03-sst-ssr-sse-decomposition.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/seqHK/dJMcafOOlqS/THrGAPVp47JgKKCLmpKpa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/seqHK/dJMcafOOlqS/THrGAPVp47JgKKCLmpKpa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/seqHK/dJMcafOOlqS/THrGAPVp47JgKKCLmpKpa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FseqHK%2FdJMcafOOlqS%2FTHrGAPVp47JgKKCLmpKpa1%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;1200&quot; height=&quot;700&quot; data-filename=&quot;03-sst-ssr-sse-decomposition.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;13. OLS 예시로 분해 확인하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확한 SST = SSR + SSE를 숫자로 확인해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력과 실제값을 다음처럼 둡니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;x = [1, 2, 3]
y = [1, 3, 8]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제값의 평균은&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;y_bar = (1 + 3 + 8) / 3 = 4&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최소제곱 회귀선을 계산하면&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;y_hat = 3.5x - 3&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;따라서 예측값은&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;y_hat = [0.5, 4, 7.5]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SST&lt;/h3&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;SST
= (1-4)^2 + (3-4)^2 + (8-4)^2
= 9 + 1 + 16
= 26&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SSR&lt;/h3&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;SSR
= (0.5-4)^2 + (4-4)^2 + (7.5-4)^2
= 12.25 + 0 + 12.25
= 24.5&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SSE&lt;/h3&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;SSE
= (1-0.5)^2 + (3-4)^2 + (8-7.5)^2
= 0.25 + 1 + 0.25
= 1.5&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인해 보면&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;SSR + SSE = 24.5 + 1.5 = 26
SST = 26&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이므로&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;SST = SSR + SSE&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 정확히 성립합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;14. R&amp;sup2;: 평균 기준보다 얼마나 좋아졌는가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R&amp;sup2;는 결정계수, coefficient of determination이라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R&amp;sup2;는 회귀모델이 평균만 예측하는 기준 모델보다 얼마나 잘했는지를 나타냅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기준 모델의 오차는 SST입니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;기준 모델:
모든 데이터에 y_bar 예측

기준 모델의 SSE:
sum_i (y_i - y_bar)^2 = SST&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회귀모델의 오차는 SSE입니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;회귀모델:
x를 사용해 y_hat_i 예측

회귀모델의 SSE:
sum_i (y_i - y_hat_i)^2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 R&amp;sup2;는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;R^2 = 1 - SSE/SST&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SST = SSR + SSE이므로 다음과 같이 쓸 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;R^2 = SSR/SST&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 식은 같은 의미입니다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;R^2
= 1 - 모델이 남긴 오차 / 평균 기준의 전체 오차
= 모델이 설명한 변동 / 전체 변동&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;숫자 예시&lt;/h3&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;SST = 100
SSE = 20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이라면&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;R^2 = 1 - 20/100
    = 0.8&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;이는 다음과 같이 해석할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평균만 사용했을 때보다 회귀모델이 y의 변동을 약 80% 더 설명했다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;y가 평균에서 흩어진 전체 변동 중 약 80%가 회귀모델의 예측으로 설명된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;15. R&amp;sup2;의 범위와 주의점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;훈련 데이터에서 절편을 포함한 최소제곱 선형회귀를 사용하면 보통&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;0 &amp;lt;= R^2 &amp;lt;= 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;하지만 다음 상황에서는 R&amp;sup2;가 음수가 될 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;절편을 제외한 회귀
테스트 데이터 평가
모델이 평균 기준보다 못함
일반적인 비선형 모델&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R&amp;sup2;가 음수라는 것은 모델의 SSE가 평균만 예측하는 기준 모델의 SST보다 크다는 뜻입니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;SSE &amp;gt; SST
-&amp;gt; 1 - SSE/SST &amp;lt; 0&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;R&amp;sup2;가 높다고 항상 좋은 모델일까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇지는 않습니다.&lt;/p&gt;
&lt;pre class=&quot;tp&quot;&gt;&lt;code&gt;R&amp;sup2;는 인과관계를 말하지 않음
R&amp;sup2;는 미래 성능을 보장하지 않음
R&amp;sup2;는 이상치에 영향을 받음
R&amp;sup2;가 높아도 과적합일 수 있음
R&amp;sup2;는 절대적인 오차 크기를 알려 주지 않음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 정답의 단위가 크게 바뀌면 SSE와 SST는 함께 변하지만, R&amp;sup2;는 같은 값을 유지할 수 있습니다. 따라서 R&amp;sup2;와 함께 RMSE, MAE, 검증 성능을 같이 보는 것이 좋습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;16. MSE와 RMSE&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSE는 데이터 개수가 많아지면 자연스럽게 커집니다. 그래서 데이터 개수로 나눈 평균제곱오차, MSE를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;MSE = SSE / n
    = (1/n) sum_i (y_i - y_hat_i)^2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MSE는 평균적인 제곱오차입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RMSE는 MSE에 제곱근을 취합니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;RMSE = sqrt(MSE)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RMSE의 단위는 y와 같습니다.&lt;/p&gt;
&lt;pre class=&quot;protobuf&quot;&gt;&lt;code&gt;y의 단위 = 만 원
MSE의 단위 = 만 원^2
RMSE의 단위 = 만 원&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;세 지표의 역할&lt;/h3&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;SSE:
전체 데이터에서 누적된 제곱오차

MSE:
데이터 하나당 평균 제곱오차

RMSE:
y의 단위로 환산한 평균적인 오차 크기

R^2:
평균 기준에 비해 변동을 얼마나 설명했는지&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;17. 선형회귀의 목적함수&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선형회귀에서는 보통 다음 목적함수를 최소화합니다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;J(w,b)
= sum_i (y_i - (w x_i + b))^2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 데이터 개수로 나눈 MSE를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;J(w,b)
= (1/n) sum_i (y_i - (w x_i + b))^2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSE와 MSE는 상수배 관계이므로 최솟값을 만드는 w와 b의 위치는 같습니다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;argmin SSE
= argmin MSE&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차이는 gradient의 크기입니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;SSE:
gradient에 n이 포함된 크기

MSE:
gradient가 1/n만큼 평균화됨&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 딥러닝 코드에서는 MSE를 평균으로 계산하는 경우가 많습니다. 샘플 수와 배치 크기에 따라 손실 규모가 크게 달라지는 것을 줄일 수 있기 때문입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;18. 행렬로 표현하는 선형회귀&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 입력 변수가 있는 다중 선형회귀는 행렬로 간단히 쓸 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;y_hat = X beta&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;절편까지 포함하려면 X의 첫 번째 열을 모두 1로 둡니다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;X =
[1 x_11 x_12 ... x_1d
 1 x_21 x_22 ... x_2d
 ...
 1 x_n1 x_n2 ... x_nd]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파라미터 벡터는&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;beta = [b, w_1, w_2, ..., w_d]^T&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;목적함수는&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;J(beta) = ||y - X beta||_2^2&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;이 식은 선형대수에서 배운 벡터 놈과 연결됩니다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;잔차 벡터 = y - X beta
SSE = ||y - X beta||_2^2&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;19. 정규방정식과 최소제곱해&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;행렬식 목적함수를 beta로 미분해 0으로 놓으면 정규방정식이 나옵니다.&lt;/p&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;X^T X beta = X^T y&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;X^T X가 가역이라면&lt;/p&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;beta_hat = (X^T X)^(-1) X^T y&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;이것을 정규방정식 해 또는 OLS( Ordinary Least Squares ) 해라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제 계산에서는 역행렬을 직접 계산하는 방식이 항상 좋은 것은 아닙니다.&lt;/p&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;X^T X가 거의 singular할 수 있음
조건수가 커질 수 있음
역행렬 계산 비용이 큼
수치 오차가 커질 수 있음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실전에서는 QR 분해, SVD, 수치 선형대수 라이브러리를 사용해 더 안정적으로 계산하는 경우가 많습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;20. 경사하강법이 필요한 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규방정식은 선형회귀의 최적해를 한 번에 계산할 수 있는 좋은 방법입니다. 그런데 데이터와 특성 수가 매우 커지면 X^T X를 만들고 역행렬을 계산하는 비용이 부담이 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 경사하강법을 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;현재 beta에서 gradient 계산
-&amp;gt; 손실이 줄어드는 방향으로 beta 이동
-&amp;gt; 반복&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경사하강법은 역행렬을 직접 계산하지 않고도 최적해에 가까워질 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;21. 선형회귀의 gradient 유도&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순 선형회귀의 목적함수를 다음처럼 두겠습니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;J(w,b) = sum_i (y_i - (w x_i + b))^2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예측값은&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;y_hat_i = w x_i + b&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;잔차를&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;e_i = y_i - y_hat_i&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 하면&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;J(w,b) = sum_i e_i^2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;w에 대한 미분&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연쇄법칙을 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;dJ/dw
= sum_i 2 e_i * de_i/dw&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잔차는&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;e_i = y_i - (w x_i + b)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이므로&lt;/p&gt;
&lt;pre class=&quot;fix&quot;&gt;&lt;code&gt;de_i/dw = -x_i&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;따라서&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;dJ/dw
= -2 sum_i x_i e_i&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;잔차 대신 예측 오차 y_hat_i - y_i를 사용하면&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;dJ/dw
= 2 sum_i x_i (y_hat_i - y_i)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 쓸 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;b에 대한 미분&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;de_i/db = -1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이므로&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;dJ/db
= -2 sum_i e_i
= 2 sum_i (y_hat_i - y_i)&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;최종 gradient는&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;gradient_w = 2 sum_i x_i (y_hat_i - y_i)
gradient_b = 2 sum_i (y_hat_i - y_i)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;22. 경사하강법 업데이트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gradient의 음의 방향으로 이동합니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;w &amp;lt;- w - eta * gradient_w
b &amp;lt;- b - eta * gradient_b&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;식을 대입하면&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;w &amp;lt;- w - eta * 2 sum_i x_i (y_hat_i - y_i)
b &amp;lt;- b - eta * 2 sum_i (y_hat_i - y_i)&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;MSE를 사용하면 1/n이 추가됩니다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;gradient_w
= (2/n) sum_i x_i (y_hat_i - y_i)

gradient_b
= (2/n) sum_i (y_hat_i - y_i)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSE와 MSE의 gradient 차이는 상수배이므로 학습률을 조절하면 같은 방향으로 학습할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;23. 경사하강법의 직관&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경사하강법은 산에서 가장 낮은 곳을 찾는 과정과 비슷합니다.&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;현재 위치에서 주변의 경사 확인
-&amp;gt; 아래쪽 방향 선택
-&amp;gt; 한 걸음 이동
-&amp;gt; 새로운 위치에서 다시 경사 확인&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선형회귀에서는 w와 b가 좌표축이 됩니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;가로축 = w
세로축 = b
높이   = SSE(w,b)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSE는 w와 b에 대한 아래로 볼록한 곡면을 만들기 때문에, 적절한 학습률을 사용하면 최솟값으로 이동할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;04-sse-contour-gradient-descent.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5qnwh/dJMcabZVrh4/qiYwyTMx3GKJo2TxID46qk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5qnwh/dJMcabZVrh4/qiYwyTMx3GKJo2TxID46qk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5qnwh/dJMcabZVrh4/qiYwyTMx3GKJo2TxID46qk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5qnwh%2FdJMcabZVrh4%2FqiYwyTMx3GKJo2TxID46qk%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;1200&quot; height=&quot;700&quot; data-filename=&quot;04-sse-contour-gradient-descent.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;등고선이 길고 좁으면 한 방향으로는 가파르고 다른 방향으로는 완만합니다. 이때 경사하강법 경로가 지그재그로 움직일 수 있습니다. 입력 특성의 스케일을 맞추면 이런 문제를 줄일 수 있습니다.&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;24. 1차원 함수로 보는 경사하강법&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;pre class=&quot;stylus&quot;&gt;&lt;code&gt;f(x) = (x - 2)^2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 함수는 꼭짓점이 x = 2인 아래로 볼록한 포물선입니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;최소점: x = 2
최솟값: f(2) = 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;x가 2에서 멀어질수록 함수값은 커집니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;x = 0 -&amp;gt; f(0) = 4
x = 1 -&amp;gt; f(1) = 1
x = 2 -&amp;gt; f(2) = 0
x = 3 -&amp;gt; f(3) = 1
x = 5 -&amp;gt; f(5) = 9&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;05-gradient-descent-parabola-x2.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mEpnU/dJMcabyHQV2/0hHKBg3WYept8NLqWWmrKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mEpnU/dJMcabyHQV2/0hHKBg3WYept8NLqWWmrKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mEpnU/dJMcabyHQV2/0hHKBg3WYept8NLqWWmrKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmEpnU%2FdJMcabyHQV2%2F0hHKBg3WYept8NLqWWmrKk%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;1200&quot; height=&quot;700&quot; data-filename=&quot;05-gradient-descent-parabola-x2.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;미분하면 어느 방향으로 가야 하는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수를 미분하면&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;f'(x) = 2(x - 2)&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;gradient, 1차원에서는 derivative는 함수가 증가하는 방향과 크기를 알려 줍니다.&lt;/p&gt;
&lt;pre class=&quot;llvm&quot;&gt;&lt;code&gt;x &amp;gt; 2:
f'(x) &amp;gt; 0
오른쪽으로 갈수록 함수가 증가하므로 왼쪽으로 이동해야 함

x &amp;lt; 2:
f'(x) &amp;lt; 0
왼쪽으로 갈수록 함수가 증가하므로 오른쪽으로 이동해야 함

x = 2:
f'(x) = 0
더 이상 이동할 필요가 없음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 gradient의 반대 방향으로 이동하면 항상 최소점 방향으로 움직이게 됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;업데이트 식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경사하강법의 업데이트 식은&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;x_(t+1) = x_t - eta f'(x_t)&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;f'(x) = 2(x-2)를 대입하면&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;x_(t+1)
= x_t - eta * 2(x_t - 2)&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;여기서 eta는 학습률입니다. 이번에는&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;x_0 = 5
eta = 0.25&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 시작해 보겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실제 반복 계산&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 step에서&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;f'(5) = 2(5-2) = 6

x_1 = 5 - 0.25 * 6
    = 3.5&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;두 번째 step에서는&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;f'(3.5) = 2(3.5-2) = 3

x_2 = 3.5 - 0.25 * 3
    = 2.75&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;계속 반복하면&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;x_0 = 5
x_1 = 3.5
x_2 = 2.75
x_3 = 2.375
x_4 = 2.1875&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처럼 최소점 2에 가까워집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수값도 함께 계산해 보면&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;f(x_0) = f(5)      = 9
f(x_1) = f(3.5)    = 2.25
f(x_2) = f(2.75)   = 0.5625
f(x_3) = f(2.375)  = 0.140625
f(x_4) = f(2.1875) = 0.03515625&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;x는 2에 가까워짐
f(x)는 0에 가까워짐&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;06-gradient-descent-convergence.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/S3sbl/dJMcaas8U8h/KVB5LvBBp5OApzyppIJmk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/S3sbl/dJMcaas8U8h/KVB5LvBBp5OApzyppIJmk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/S3sbl/dJMcaas8U8h/KVB5LvBBp5OApzyppIJmk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FS3sbl%2FdJMcaas8U8h%2FKVB5LvBBp5OApzyppIJmk1%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;1200&quot; height=&quot;700&quot; data-filename=&quot;06-gradient-descent-convergence.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;왜 최소점에 가까워지는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 함수에서는 업데이트 식을 직접 정리할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;x_(t+1)
= x_t - 2eta(x_t - 2)
= (1 - 2eta)x_t + 4eta&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최소점 2에서의 차이를 보면 더 간단합니다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;x_(t+1) - 2
= (1 - 2eta)(x_t - 2)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;|1 - 2eta| &amp;lt; 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이면 차이가 반복해서 작아지고 x_t는 2로 수렴합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 안정적인 학습률 조건은&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;0 &amp;lt; eta &amp;lt; 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;학습률에 따른 차이&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;eta가 너무 작음:
x가 2에 아주 천천히 접근

eta = 0.25:
x가 안정적으로 2에 접근

eta = 0.75:
최소점 주변을 번갈아 움직이며 접근

eta = 1:
반대편으로 같은 거리를 이동해 수렴하지 않음

eta &amp;gt; 1:
점점 최소점에서 멀어져 발산&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 간단한 예시는 선형회귀의 SSE에도 그대로 연결됩니다. 선형회귀에서는 x 하나 대신 w와 b가 있고, f(x) 하나 대신 SSE(w,b)라는 곡면을 최소화합니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;1차원 예시:
f(x) = (x-2)^2
최소점 x = 2

선형회귀:
SSE(w,b) = sum_i (y_i - (w x_i + b))^2
최소점 (w*, b*)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 선형회귀의 경사하강법도 같은 원리입니다.&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;현재 파라미터 위치 확인
-&amp;gt; SSE의 gradient 계산
-&amp;gt; gradient 반대 방향으로 이동
-&amp;gt; SSE가 줄었는지 확인
-&amp;gt; 반복&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;25. 숫자로 보는 경사하강법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 데이터로 한 번 업데이트해 보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;x = [1, 2, 3]
y = [1, 3, 8]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 파라미터를&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;w = 0
b = 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;으로 두면 예측값은&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;y_hat = [0, 0, 0]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;초기 SSE&lt;/h3&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;SSE
= (1-0)^2 + (3-0)^2 + (8-0)^2
= 1 + 9 + 64
= 74&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;초기 gradient&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예측값에서 실제값을 뺀 값은&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;y_hat - y = [-1, -3, -8]&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;w에 대한 gradient는&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;gradient_w
= 2[1(-1) + 2(-3) + 3(-8)]
= 2[-1 - 6 - 24]
= -62&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;b에 대한 gradient는&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;gradient_b
= 2[-1 - 3 - 8]
= -24&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;학습률을 eta = 0.01로 하면&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;w_new = 0 - 0.01(-62) = 0.62
b_new = 0 - 0.01(-24) = 0.24&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;새로운 예측값은&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;y_hat_new
= 0.62x + 0.24
= [0.86, 1.48, 2.10]&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;새 SSE는&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;SSE_new
= (1-0.86)^2 + (3-1.48)^2 + (8-2.10)^2
&amp;asymp; 37.14&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;한 번의 업데이트만으로도&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;기존 SSE = 74
새로운 SSE &amp;asymp; 37.14&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;다음 step에서도 같은 과정을 반복합니다.&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;gradient 계산
-&amp;gt; w, b 업데이트
-&amp;gt; 예측
-&amp;gt; SSE 확인&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;26. 학습률이 너무 크거나 작으면?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학습률 eta는 한 번에 움직이는 크기입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;학습률이 너무 작음&lt;/h3&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;업데이트 폭이 너무 작음
손실이 천천히 감소
많은 반복이 필요&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;학습률이 적절함&lt;/h3&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;손실이 안정적으로 감소
최솟값 주변으로 접근&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;학습률이 너무 큼&lt;/h3&gt;
&lt;pre class=&quot;x86asm&quot;&gt;&lt;code&gt;최솟값을 지나침
손실이 크게 진동
SSE가 발산
파라미터가 inf 또는 NaN이 됨&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;07-learning-rate-comparison.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ltE7P/dJMcafH1maZ/KUJjhQA5uKCkX4jJ5acaP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ltE7P/dJMcafH1maZ/KUJjhQA5uKCkX4jJ5acaP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ltE7P/dJMcafH1maZ/KUJjhQA5uKCkX4jJ5acaP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FltE7P%2FdJMcafH1maZ%2FKUJjhQA5uKCkX4jJ5acaP1%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;1200&quot; height=&quot;700&quot; data-filename=&quot;07-learning-rate-comparison.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실전에서는 처음부터 하나의 학습률을 완벽하게 맞히기보다, 여러 후보를 비교하며 손실 곡선을 확인합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;27. Batch Gradient Descent와 SGD&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 데이터의 SSE gradient를 매번 계산하면 Batch Gradient Descent입니다.&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;모든 훈련 샘플 사용
-&amp;gt; 전체 gradient 계산
-&amp;gt; 한 번 업데이트&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;SGD는 한 샘플 또는 작은 mini-batch만 사용해 gradient를 추정합니다.&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;샘플 일부 선택
-&amp;gt; noisy gradient 계산
-&amp;gt; 자주 업데이트&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Batch Gradient Descent&lt;/h3&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;장점:
gradient가 안정적
손실 곡선이 비교적 부드러움
작은 데이터셋에 적합

단점:
대규모 데이터에서 한 step이 느림
메모리 비용이 큼&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SGD 또는 Mini-batch SGD&lt;/h3&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;장점:
대규모 데이터에 적합
업데이트가 빠름
gradient noise가 탐색에 도움을 줄 수 있음

단점:
손실 곡선이 흔들림
학습률과 batch size에 민감
최솟값 주변에서 계속 움직일 수 있음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딥러닝에서는 보통 Mini-batch SGD를 사용합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;28. 선형회귀에서의 정규화&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특성이 많거나 서로 강하게 상관되어 있으면 모델이 큰 가중치를 사용해 훈련 데이터에 맞을 수 있습니다. 이때 정규화를 추가할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Ridge 회귀&lt;/h3&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;J(beta)
= ||y - X beta||_2^2
+ lambda ||beta||_2^2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;L2 패널티는 큰 가중치를 줄입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Lasso 회귀&lt;/h3&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;J(beta)
= ||y - X beta||_2^2
+ lambda ||beta||_1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;L1 패널티는 일부 가중치를 정확히 0으로 만들 수 있어 feature selection 효과가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;절편에는 정규화를 적용하지 않는 경우가 많습니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;가중치 w_1, ..., w_d:
정규화 적용

절편 b:
정규화에서 제외하는 경우가 많음&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;29. 잔차를 확인하는 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSE가 작다는 것만으로 모델이 적절하다고 단정할 수 없습니다. 잔차를 그래프로 확인하면 모델이 놓친 구조를 발견할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 선형회귀의 잔차는 대체로 다음 특성을 기대합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;잔차의 평균이 0에 가까움
x에 따라 특정한 패턴이 없음
잔차의 분산이 지나치게 변하지 않음
잔차끼리 강한 상관이 없음&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;잔차에 곡선 패턴이 있음&lt;/h3&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;선형모델이 실제 비선형 관계를 놓쳤을 가능성&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;x가 커질수록 잔차의 폭이 커짐&lt;/h3&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;이분산성 가능성
로그 변환 또는 다른 모델 검토&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;일부 점만 잔차가 매우 큼&lt;/h3&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;이상치 또는 데이터 입력 오류 확인&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;08-residual-diagnostics.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Q6pC5/dJMcabS2f69/imYhrmKDrKVGoMx9ZCyBvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Q6pC5/dJMcabS2f69/imYhrmKDrKVGoMx9ZCyBvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Q6pC5/dJMcabS2f69/imYhrmKDrKVGoMx9ZCyBvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQ6pC5%2FdJMcabS2f69%2FimYhrmKDrKVGoMx9ZCyBvK%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;1200&quot; height=&quot;700&quot; data-filename=&quot;08-residual-diagnostics.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;30. 평균 기준, 회귀선, R&amp;sup2;를 한 번에 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선형회귀에서 평균은 매우 중요한 기준선입니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;평균 모델:
y_hat_i = y_bar
SSE_baseline = SST&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회귀모델은 x를 사용해 평균보다 정교한 예측을 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;회귀 모델:
y_hat_i = w x_i + b
SSE_model = SSE&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 모델을 비교하면&lt;/p&gt;
&lt;pre class=&quot;fix&quot;&gt;&lt;code&gt;평균 기준의 전체 오차 = SST
회귀모델이 남긴 오차 = SSE
모델이 줄인 오차 = SST - SSE = SSR&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;R^2 = (SST - SSE) / SST
    = SSR / SST
    = 1 - SSE/SST&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;이 식을 말로 바꾸면 다음과 같습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R&amp;sup2;는 x를 사용한 회귀모델이 평균만 예측하는 기준 모델에 비해 y의 변동을 얼마나 더 설명하는지를 나타내는 비율이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;31. 선형회귀 학습 전체 흐름&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선형회귀의 전체 과정을 정리하면 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 입력 x와 정답 y를 준비한다.
2. y_hat = w x + b로 예측한다.
3. 잔차 y - y_hat을 계산한다.
4. SSE 또는 MSE를 계산한다.
5. w와 b에 대한 gradient를 계산한다.
6. w와 b를 gradient의 반대 방향으로 업데이트한다.
7. 손실이 줄어드는지 확인한다.
8. 검증 데이터에서 RMSE와 R&amp;sup2;를 확인한다.
9. 잔차 그래프로 놓친 패턴을 확인한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파라미터를 직접 계산하는 정규방정식과 반복적으로 업데이트하는 경사하강법은 서로 다른 방법이지만 목표는 같습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;정규방정식:
최적해를 수식으로 직접 계산

경사하강법:
gradient를 따라 반복적으로 최적해에 접근&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;32. 파이썬으로 구현하는 선형회귀와 경사하강법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 예시는 외부 머신러닝 라이브러리 없이 NumPy만 사용해 선형회귀를 학습하는 코드입니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;import numpy as np

# 입력과 정답
x = np.array([1.0, 2.0, 3.0])
y = np.array([1.0, 3.0, 8.0])

# 파라미터 초기화
w = 0.0
b = 0.0

learning_rate = 0.01
epochs = 1000

for epoch in range(epochs):
    # 예측
    y_hat = w * x + b

    # 잔차와 손실
    error = y_hat - y
    mse = np.mean(error ** 2)

    # MSE gradient
    grad_w = 2 * np.mean(x * error)
    grad_b = 2 * np.mean(error)

    # 파라미터 업데이트
    w -= learning_rate * grad_w
    b -= learning_rate * grad_b

print(&quot;w =&quot;, w)
print(&quot;b =&quot;, b)

# 최종 예측
y_hat = w * x + b
sse = np.sum((y - y_hat) ** 2)
sst = np.sum((y - np.mean(y)) ** 2)
r2 = 1 - sse / sst

print(&quot;SSE =&quot;, sse)
print(&quot;R^2 =&quot;, r2)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드에서 중요한 부분은 다음입니다.&lt;/p&gt;
&lt;pre class=&quot;subunit&quot;&gt;&lt;code&gt;error = y_hat - y
grad_w = 2 * mean(x * error)
grad_b = 2 * mean(error)
w -= learning_rate * grad_w
b -= learning_rate * grad_b&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평균을 사용한 기준 모델의 SSE도 함께 계산할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;baseline_prediction = np.full_like(y, np.mean(y))
baseline_sse = np.sum((y - baseline_prediction) ** 2)

print(&quot;baseline SSE =&quot;, baseline_sse)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R&amp;sup2;는 회귀모델이 이 기준 모델보다 얼마나 나아졌는지를 계산합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;33. 자주 하는 실수&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. SSE와 SSR의 이름을 반대로 기억&lt;/h3&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;SSE:
Error, 남은 오차

SSR:
Regression 또는 Explained, 설명된 변동&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문헌에 따라 SSR을 Residual Sum of Squares로 표기하는 경우도 있어 약어만 보지 말고 정의를 확인해야 합니다. 혼동을 피하려면 다음 표현도 함께 기억하면 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Total Sum of Squares = SST
Explained Sum of Squares = SSR 또는 ESS
Residual Sum of Squares = SSE 또는 RSS&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 평균 기준을 무시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R&amp;sup2;는 절대적인 정확도가 아니라 평균 기준과의 비교입니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;R^2 = 기준 모델보다 얼마나 좋아졌는가&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 훈련 R&amp;sup2;만 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;훈련 데이터에서 R&amp;sup2;가 높아도 새로운 데이터에서 성능이 낮을 수 있습니다. 검증 세트와 테스트 세트를 분리해야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. feature scaling을 하지 않음&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특성마다 크기가 크게 다르면 SSE 곡면의 조건수가 커지고 gradient descent가 느려질 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;표준화 예시:
x_scaled = (x - mean(x)) / std(x)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 이상치를 그대로 둠&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제곱오차는 큰 오차를 강하게 벌점 줍니다. 이상치가 중요한 관측인지 입력 오류인지 확인해야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;34. 이번 글의 핵심 요약&lt;/h2&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;선형회귀:
y_hat = w x + b

잔차:
e_i = y_i - y_hat_i

SSE:
sum_i (y_i - y_hat_i)^2

평균 기준:
제곱오차를 최소화하는 상수 예측값은 평균

SST:
sum_i (y_i - y_bar)^2
전체 변동량

SSR:
sum_i (y_hat_i - y_bar)^2
모델이 설명한 변동량

SSE:
sum_i (y_i - y_hat_i)^2
모델이 설명하지 못한 변동량

분해:
SST = SSR + SSE

결정계수:
R^2 = 1 - SSE/SST = SSR/SST

경사하강법:
gradient를 계산하고 그 반대 방향으로 w와 b를 반복 업데이트&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선형회귀는 단순히 직선을 그리는 모델이 아닙니다. 평균을 기준으로 전체 변동을 정의하고, 회귀선이 설명한 변동과 남은 오차를 나누며, SSE 또는 MSE를 gradient descent로 줄이는 하나의 완성된 머신러닝 학습 구조입니다.&lt;/p&gt;</description>
      <category>ML</category>
      <author>rem1004</author>
      <guid isPermaLink="true">https://rem1004.tistory.com/51</guid>
      <comments>https://rem1004.tistory.com/51#entry51comment</comments>
      <pubDate>Fri, 4 Sep 2026 10:09:31 +0900</pubDate>
    </item>
    <item>
      <title>4. Self-Attention과 Transformer Encoder 완전 정복</title>
      <link>https://rem1004.tistory.com/50</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Self-Attention의 목적은 각 토큰이 문장 안의 다른 토큰을 참고해 새로운 표현을 만드는 것이다. 같은 단어라도 주변 문맥에 따라 의미가 달라지는데, 그 관계를 계산하는 세 역할이 &lt;b&gt;Query&lt;/b&gt;, &lt;b&gt;Key&lt;/b&gt;, &lt;b&gt;Value&lt;/b&gt;다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Query와 Key의 Score에서 Value의 Weighted Sum까지 이어지는 Attention 계산&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;TTT-04-qkv-attention.png&quot; data-origin-width=&quot;2056&quot; data-origin-height=&quot;765&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k8XfE/dJMcaiSbdYd/0CzPzXeoUXNOckCEYFJfWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k8XfE/dJMcaiSbdYd/0CzPzXeoUXNOckCEYFJfWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k8XfE/dJMcaiSbdYd/0CzPzXeoUXNOckCEYFJfWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk8XfE%2FdJMcaiSbdYd%2F0CzPzXeoUXNOckCEYFJfWk%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;2056&quot; height=&quot;765&quot; data-filename=&quot;TTT-04-qkv-attention.png&quot; data-origin-width=&quot;2056&quot; data-origin-height=&quot;765&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Query, Key, Value의 역할&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력 행렬 &lt;code&gt;X&lt;/code&gt;에 서로 다른 학습 가중치를 곱해 세 행렬을 만든다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;Q = XW_Q
K = XW_K
V = XW_V&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Query (Q)&lt;/b&gt;: 현재 토큰이 찾고 싶은 정보&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Key (K)&lt;/b&gt;: 각 토큰이 가진 정보의 표지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Value (V)&lt;/b&gt;: 실제로 가져와 섞을 내용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Query와 모든 Key의 내적은 관련도 점수가 된다. 점수가 클수록 해당 Value를 더 많이 반영한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Q, K, V는 서로 다른 데이터 세 벌이 아니라 같은 입력 &lt;code&gt;X&lt;/code&gt;를 서로 다른 관점으로 투영한 결과다. &lt;code&gt;W_Q&lt;/code&gt;, &lt;code&gt;W_K&lt;/code&gt;, &lt;code&gt;W_V&lt;/code&gt;가 학습되면서 모델은 &amp;ldquo;무엇을 질문으로 만들고, 무엇을 검색 표지로 삼고, 어떤 내용을 전달할지&amp;rdquo;를 함께 배운다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Scaled Dot-Product Attention&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transformer의 핵심 수식은 다음 한 줄이다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;Attention(Q, K, V) = softmax(QKᵀ / &amp;radic;d_k) V&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계산 순서는 네 단계다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;QKᵀ&lt;/code&gt;: 모든 토큰 쌍의 유사도를 계산한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/ &amp;radic;d_k&lt;/code&gt;: 차원이 커질수록 내적값이 과도하게 커지는 것을 막는다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;softmax&lt;/code&gt;: 각 Query가 Key들에 주는 가중치의 합을 1로 만든다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;times; V&lt;/code&gt;: 가중치에 따라 Value를 섞어 문맥화된 표현을 만든다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;&amp;radic;d_k&lt;/code&gt;로 나누지 않으면 큰 내적값 때문에 Softmax가 지나치게 뾰족해지고 Gradient가 작아질 수 있다. 그래서 이름에도 &lt;b&gt;Scaled&lt;/b&gt;가 붙는다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Shape로 따라가는 Attention&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Batch와 Head 차원을 잠시 제외하고, 토큰 수가 &lt;code&gt;n&lt;/code&gt;, Query/Key 차원이 &lt;code&gt;d_k&lt;/code&gt;, Value 차원이 &lt;code&gt;d_v&lt;/code&gt;라면 다음과 같다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Matrix&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;Shape&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Q&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;&lt;code&gt;n &amp;times; d_k&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;토큰별 Query&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;K&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;&lt;code&gt;n &amp;times; d_k&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;토큰별 Key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;QKᵀ&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;&lt;code&gt;n &amp;times; n&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;모든 토큰 쌍의 Score&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;V&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;&lt;code&gt;n &amp;times; d_v&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;토큰별 Value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;softmax(QKᵀ)V&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;&lt;code&gt;n &amp;times; d_v&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;문맥화된 출력&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;QKᵀ&lt;/code&gt;의 각 행은 한 Query가 모든 Key를 바라본 Score다. 따라서 Softmax는 보통 마지막 축, 즉 행마다 적용한다. 축을 잘못 잡으면 전혀 다른 의미의 Weight가 만들어진다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;작은 문장으로 보는 계산&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;저는 / 강사 / 입니다&amp;rdquo; 세 토큰이라면 Score Matrix는 &lt;code&gt;3&amp;times;3&lt;/code&gt;이다. &amp;ldquo;강사&amp;rdquo; Query의 행에는 &amp;ldquo;저는&amp;rdquo;, &amp;ldquo;강사&amp;rdquo;, &amp;ldquo;입니다&amp;rdquo; Key와의 관련도가 들어간다. Softmax 뒤 세 Weight의 합은 1이 되고, 이 Weight로 세 Value를 섞어 새로운 &amp;ldquo;강사&amp;rdquo; 표현을 만든다. Self-Attention 출력도 토큰 수 3을 유지한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Multi-Head Attention&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 종류의 Attention만으로는 모든 관계를 표현하기 어렵다. &lt;b&gt;Multi-Head Attention (MHA)&lt;/b&gt;은 Q, K, V를 여러 저차원 공간으로 각각 투영해 Attention을 병렬로 계산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원 논문의 대표 설정은 &lt;code&gt;d_model = 512&lt;/code&gt;, &lt;code&gt;h = 8&lt;/code&gt;, &lt;code&gt;d_k = 64&lt;/code&gt;다. 8개의 Head가 각자 관계를 찾고, 결과를 &lt;b&gt;Concatenation&lt;/b&gt;한 뒤 다시 Linear Layer를 통과시켜 512차원으로 되돌린다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;MultiHead(Q,K,V) = Concat(head_1, ..., head_h) W_O&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 다른 Head는 문법적 관계, 장거리 의존성, 특정 의미 연결 등 서로 다른 패턴에 집중할 수 있다. 단, 각 Head가 반드시 사람이 이름 붙일 수 있는 한 가지 역할만 맡는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Head 수를 8배 늘렸다고 총 표현 차원이 8배가 되는 것은 아니다. 보통 &lt;code&gt;d_model&lt;/code&gt;을 Head 수로 나눠 Head당 차원을 줄인다. &lt;code&gt;8 &amp;times; 64 = 512&lt;/code&gt;인 이유다. 따라서 Head 수는 표현 공간을 병렬 Subspace로 나누는 Hyperparameter이지, 무조건 연산량을 8배 키우는 설정은 아니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Multi-Head Attention과 FFN, Residual Path를 포함한 Transformer Encoder Layer&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;TTT-04-encoder-layer.png&quot; data-origin-width=&quot;2007&quot; data-origin-height=&quot;784&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NK0BE/dJMcacxCOeG/ugjKgB7U1ks5kAnin4v0OK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NK0BE/dJMcacxCOeG/ugjKgB7U1ks5kAnin4v0OK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NK0BE/dJMcacxCOeG/ugjKgB7U1ks5kAnin4v0OK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNK0BE%2FdJMcacxCOeG%2FugjKgB7U1ks5kAnin4v0OK%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;2007&quot; height=&quot;784&quot; data-filename=&quot;TTT-04-encoder-layer.png&quot; data-origin-width=&quot;2007&quot; data-origin-height=&quot;784&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. Encoder Layer의 전체 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 Transformer Encoder Layer는 크게 두 블록으로 구성된다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Input
  &amp;darr;
Multi-Head Self-Attention
  &amp;darr;
Add &amp;amp; Norm
  &amp;darr;
Position-wise Feed-Forward Network
  &amp;darr;
Add &amp;amp; Norm&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 블록에는 입력을 출력에 더하는 &lt;b&gt;Residual Connection (Skip Connection)&lt;/b&gt;이 있다. 이 연결은 Gradient 흐름을 돕고 깊은 네트워크의 학습을 안정화한다. 이어지는 &lt;b&gt;Layer Normalization&lt;/b&gt;은 각 토큰 표현의 feature 차원을 정규화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Position-wise Feed-Forward Network (FFN)&lt;/b&gt;는 모든 위치에 동일한 MLP를 독립적으로 적용한다. 원 논문에서는 대략 &lt;code&gt;512 &amp;rarr; 2048 &amp;rarr; 512&lt;/code&gt; 구조와 ReLU를 사용했다. Self-Attention이 토큰 사이의 정보를 섞는다면, FFN은 각 토큰 내부 feature를 비선형적으로 변환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Encoder Layer를 여러 번, 원 논문에서는 6회 쌓는다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Residual Connection의 Shape 규칙&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Residual Addition을 하려면 입력과 서브레이어 출력 Shape가 같아야 한다. 그래서 MHA의 최종 Projection도 &lt;code&gt;d_model&lt;/code&gt;로 돌아오고, FFN도 내부에서 &lt;code&gt;d_ff&lt;/code&gt;로 넓혔다가 다시 &lt;code&gt;d_model&lt;/code&gt;로 줄인다. &lt;code&gt;512 &amp;rarr; 2048 &amp;rarr; 512&lt;/code&gt;는 단순 관례가 아니라 Residual Path와 Shape를 맞추는 구조다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Layer Normalization과 Batch Normalization&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CNN에서 자주 쓰는 Batch Normalization은 Batch 통계에 의존한다. Layer Normalization은 각 Sample의 Token 표현 내부 Feature를 정규화하므로 문장 길이와 Batch 크기가 변하는 Sequence Model에 더 자연스럽다. 원 논문 구조는 서브레이어 뒤에 Norm을 두는 &lt;b&gt;Post-Norm&lt;/b&gt;이며, 현대의 깊은 Transformer는 학습 안정성을 위해 서브레이어 앞에 Norm을 두는 &lt;b&gt;Pre-Norm&lt;/b&gt;도 많이 사용한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. &amp;ldquo;Attention이 전부&amp;rdquo;라는 말의 정확한 의미&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transformer에는 Attention만 있는 것이 아니다. Embedding, Positional Encoding, Linear Projection, Softmax, FFN, Residual Connection, Normalization, Dropout도 필요하다. &amp;ldquo;Attention Is All You Need&amp;rdquo;는 순서 정보를 처리하기 위해 recurrence나 convolution이 필수는 아니라는 뜻에 가깝다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 계산 복잡도와 병렬성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Self-Attention은 모든 토큰 쌍을 비교하므로 Sequence Length에 대해 &lt;code&gt;O(n&amp;sup2;)&lt;/code&gt; Score Memory가 필요하다. 반면 각 토큰의 Q, K, V를 한꺼번에 Matrix Multiplication으로 계산할 수 있어 GPU 병렬화에 매우 적합하다. RNN은 한 Step의 비용은 작을 수 있어도 &lt;code&gt;h_t&lt;/code&gt;가 &lt;code&gt;h_(t-1)&lt;/code&gt;에 의존하므로 시간축 병렬화가 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &amp;ldquo;Transformer가 언제나 계산량이 적다&amp;rdquo;가 아니라 &amp;ldquo;훈련 시 병렬 계산이 쉽고 토큰 간 경로가 짧다&amp;rdquo;가 정확한 장점이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;구현 순서 체크리스트&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Input &lt;code&gt;X&lt;/code&gt;에서 Linear Projection으로 Q, K, V를 만든다.&lt;/li&gt;
&lt;li&gt;Head 차원을 분리하고 &lt;code&gt;QKᵀ&lt;/code&gt;를 계산한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;radic;d_k&lt;/code&gt;로 Scale하고 필요한 Mask를 더한다.&lt;/li&gt;
&lt;li&gt;Key 축으로 Softmax와 Dropout을 적용한다.&lt;/li&gt;
&lt;li&gt;Weight와 V를 곱한다.&lt;/li&gt;
&lt;li&gt;Head를 Concatenate하고 Output Projection을 적용한다.&lt;/li&gt;
&lt;li&gt;Residual Addition과 Layer Normalization을 수행한다.&lt;/li&gt;
&lt;li&gt;FFN 뒤에도 Residual Addition과 Normalization을 적용한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Key Terms&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Query&lt;/code&gt; &amp;middot; &lt;code&gt;Key&lt;/code&gt; &amp;middot; &lt;code&gt;Value&lt;/code&gt; &amp;middot; &lt;code&gt;Scaled Dot-Product Attention&lt;/code&gt; &amp;middot; &lt;code&gt;Softmax&lt;/code&gt; &amp;middot; &lt;code&gt;Multi-Head Attention&lt;/code&gt; &amp;middot; &lt;code&gt;Concatenation&lt;/code&gt; &amp;middot; &lt;code&gt;Residual Connection&lt;/code&gt; &amp;middot; &lt;code&gt;Layer Normalization&lt;/code&gt; &amp;middot; &lt;code&gt;Feed-Forward Network&lt;/code&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Self Check&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Attention Score를 &lt;code&gt;&amp;radic;d_k&lt;/code&gt;로 나누는 이유는 무엇인가?&lt;/li&gt;
&lt;li&gt;Self-Attention과 FFN은 각각 어떤 방향으로 정보를 섞는가?&lt;/li&gt;
&lt;li&gt;Residual Connection이 깊은 모델의 학습을 돕는 이유는 무엇인가?&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>DL/Transformer</category>
      <author>rem1004</author>
      <guid isPermaLink="true">https://rem1004.tistory.com/50</guid>
      <comments>https://rem1004.tistory.com/50#entry50comment</comments>
      <pubDate>Tue, 1 Sep 2026 03:32:50 +0900</pubDate>
    </item>
    <item>
      <title>5. Transformer Decoder부터 PPL&amp;middot;BLEU까지 - 학습, 추론, 평가 한 번에 정리</title>
      <link>https://rem1004.tistory.com/49</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Encoder가 입력 문장을 문맥화된 표현으로 바꾸면 Decoder는 이를 참고해 출력 토큰을 한 개씩 만든다. 학습 중에는 정답 문장을 알고 있지만 추론 중에는 미래 단어를 볼 수 없다. Transformer Decoder의 설계는 이 차이를 정확히 통제한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Causal Mask와 Cross-Attention을 거쳐 Next Token을 생성하는 Transformer Decoder&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;TTT-05-decoder-flow.png&quot; data-origin-width=&quot;1693&quot; data-origin-height=&quot;929&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3soS1/dJMcacRPjkn/IHpANTkbb2FpbPzNZbX620/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3soS1/dJMcacRPjkn/IHpANTkbb2FpbPzNZbX620/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3soS1/dJMcacRPjkn/IHpANTkbb2FpbPzNZbX620/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3soS1%2FdJMcacRPjkn%2FIHpANTkbb2FpbPzNZbX620%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;1693&quot; height=&quot;929&quot; data-filename=&quot;TTT-05-decoder-flow.png&quot; data-origin-width=&quot;1693&quot; data-origin-height=&quot;929&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Masked Multi-Head Self-Attention&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Decoder의 첫 블록은 &lt;b&gt;Masked Multi-Head Self-Attention&lt;/b&gt;이다. 각 위치는 자신과 그 이전 토큰만 볼 수 있고 미래 토큰은 볼 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Attention Score 행렬에서 미래 위치에 매우 작은 값, 실무에서는 보통 &lt;code&gt;-&amp;infin;&lt;/code&gt;에 해당하는 값을 더한다. Softmax를 통과하면 그 위치의 가중치가 0이 된다. 이를 &lt;b&gt;Causal Mask&lt;/b&gt; 또는 &lt;b&gt;Look-ahead Mask&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Target 길이가 4라면 허용 관계는 아래와 같은 Lower Triangular Matrix가 된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;         Key position
Query      1   2   3   4
  1        O   X   X   X
  2        O   O   X   X
  3        O   O   O   X
  4        O   O   O   O&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Score에는 허용 위치에 0, 차단 위치에 &lt;code&gt;-&amp;infin;&lt;/code&gt;를 더한다. Mask는 Q나 K 벡터를 삭제하는 것이 아니라 Softmax 직전 Score를 바꾼다. Padding Mask까지 있다면 두 Mask를 결합해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학습 때 정답 시퀀스를 한꺼번에 넣더라도 Mask 덕분에 모델은 미래 정답을 훔쳐보지 못한다. 이 구조가 &lt;b&gt;Autoregressive Generation&lt;/b&gt;을 학습 가능하게 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Encoder-Decoder Attention&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Decoder의 두 번째 Attention 블록은 &lt;b&gt;Cross-Attention&lt;/b&gt;이라고도 부른다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Query: Decoder의 현재 표현&lt;/li&gt;
&lt;li&gt;Key, Value: Encoder의 최종 출력&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;CrossAttention(Q_decoder, K_encoder, V_encoder)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Decoder가 &amp;ldquo;지금 만들 단어&amp;rdquo;의 관점에서 입력 문장의 어떤 부분을 참고할지 결정하는 단계다. 이후 FFN을 통과하며, 각 서브레이어에는 Encoder와 마찬가지로 Residual Connection과 Layer Normalization이 적용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source 길이가 &lt;code&gt;n&lt;/code&gt;, Target 길이가 &lt;code&gt;m&lt;/code&gt;이라면 Cross-Attention Score Shape는 &lt;code&gt;m &amp;times; n&lt;/code&gt;이다. Self-Attention의 정사각형 &lt;code&gt;n &amp;times; n&lt;/code&gt;과 다르게 Source와 Target 길이가 달라도 된다. 이 행렬의 각 행은 한 Target 위치가 모든 Source 위치를 얼마나 참고하는지 나타낸다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 학습과 추론의 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학습에서는 정답 문장을 한 칸 오른쪽으로 민 &lt;b&gt;Shifted Target&lt;/b&gt;을 Decoder 입력으로 사용한다. 모든 위치의 다음 토큰 Loss를 동시에 계산할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추론은 순차적이다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;&amp;lt;sos&amp;gt;                       &amp;rarr; I
&amp;lt;sos&amp;gt; I                     &amp;rarr; am
&amp;lt;sos&amp;gt; I am                  &amp;rarr; an
&amp;lt;sos&amp;gt; I am an               &amp;rarr; instructor
&amp;lt;sos&amp;gt; I am an instructor    &amp;rarr; &amp;lt;eos&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 단계에서 마지막 Linear Layer가 Decoder 표현을 어휘 크기의 &lt;b&gt;Logits&lt;/b&gt;로 바꾸고, Softmax가 다음 토큰 확률을 만든다. 가장 큰 값만 고르는 &lt;b&gt;Greedy Decoding&lt;/b&gt; 외에도 여러 후보를 유지하는 &lt;b&gt;Beam Search&lt;/b&gt;를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Greedy Decoding은 매 Step의 최선만 선택해 빠르지만, 현재의 최선이 전체 문장의 최선이라는 보장은 없다. Beam Search는 누적 Log Probability가 높은 &lt;code&gt;B&lt;/code&gt;개 후보를 유지한다. Beam Width가 커질수록 탐색은 넓어지지만 계산량과 메모리가 증가하고, 짧은 문장을 과도하게 선호할 수 있어 &lt;b&gt;Length Penalty&lt;/b&gt;를 함께 사용하기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학습 시에는 모든 Target 위치의 정답을 알고 있어 Masked Attention과 Loss 계산을 병렬화할 수 있다. 그러나 추론 시 다음 입력 Token이 직전 출력에 달려 있으므로 Autoregressive Decoding은 기본적으로 순차적이다. Encoder의 병렬성과 Decoder 추론의 직렬성을 구분해야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. Dropout과 비선형성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원 Transformer는 여러 위치에서 &lt;code&gt;Dropout = 0.1&lt;/code&gt;을 사용한다. Attention Weight, 서브레이어 출력, Embedding과 Positional Encoding의 합 등에 Dropout을 적용해 &lt;b&gt;Overfitting&lt;/b&gt;을 줄인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Attention 수식 자체에는 ReLU가 없지만 모델 전체가 선형인 것은 아니다. Q와 K의 곱, Softmax, FFN의 Activation이 비선형성을 만든다. 즉, &amp;ldquo;Attention 블록에 ReLU가 없다&amp;rdquo;와 &amp;ldquo;Transformer가 선형 모델이다&amp;rdquo;는 전혀 다른 말이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Perplexity와 BLEU가 평가하는 서로 다른 요소&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;TTT-05-ppl-vs-bleu.png&quot; data-origin-width=&quot;1774&quot; data-origin-height=&quot;887&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAffI9/dJMcagNEMDl/j47kG7g4u8snHRPKK0BIKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAffI9/dJMcagNEMDl/j47kG7g4u8snHRPKK0BIKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAffI9/dJMcagNEMDl/j47kG7g4u8snHRPKK0BIKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAffI9%2FdJMcagNEMDl%2Fj47kG7g4u8snHRPKK0BIKk%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;1774&quot; height=&quot;887&quot; data-filename=&quot;TTT-05-ppl-vs-bleu.png&quot; data-origin-width=&quot;1774&quot; data-origin-height=&quot;887&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. Perplexity (PPL)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언어 모델 평가는 흔히 &lt;b&gt;Perplexity (PPL)&lt;/b&gt;를 사용한다. 평균 Negative Log-Likelihood 또는 Cross-Entropy를 지수화한 값으로 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;PPL = exp(average cross-entropy loss)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PPL이 100이면 모델이 매 순간 평균적으로 100개의 후보 사이에서 헷갈리는 것처럼 해석할 수 있다. 낮을수록 정답 시퀀스에 높은 확률을 준다. 다만 서로 다른 Tokenization이나 데이터셋의 PPL을 단순 비교하면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정답 Token 확률이 각 Step에서 &lt;code&gt;0.5, 0.25&lt;/code&gt;라면 평균 Negative Log-Likelihood는 &lt;code&gt;-(log 0.5 + log 0.25)/2&lt;/code&gt;이고 PPL은 그 값을 지수화한 &lt;code&gt;&amp;radic;8 &amp;asymp; 2.83&lt;/code&gt;이다. PPL은 Sequence Probability의 역기하평균이므로 한두 Token에 매우 낮은 확률을 주면 크게 나빠진다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. BLEU Score&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기계 번역에서는 정답 문장과 생성 문장의 &lt;b&gt;n-gram Precision&lt;/b&gt;을 비교하는 &lt;b&gt;BLEU (Bilingual Evaluation Understudy)&lt;/b&gt;를 사용한다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;BLEU = BP &amp;times; exp(&amp;Sigma; w_n log p_n)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;p_n&lt;/code&gt;: n-gram Precision&lt;/li&gt;
&lt;li&gt;&lt;code&gt;w_n&lt;/code&gt;: n-gram별 가중치&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BP&lt;/code&gt;: 짧은 번역에 벌점을 주는 &lt;b&gt;Brevity Penalty&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 단어를 반복해 점수를 부풀리지 못하도록 정답 문장에 존재하는 횟수까지만 인정하는 &lt;b&gt;Clipped Precision&lt;/b&gt;을 쓴다. 말뭉치 전체의 n-gram 일치 수를 합산하는 &lt;b&gt;Corpus BLEU&lt;/b&gt;가 일반적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BLEU는 단어 위치가 완전히 같지 않아도 부분 일치를 볼 수 있지만, 의미 보존이나 문장 자연스러움을 완벽히 측정하지는 않는다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Clipped Precision이 필요한 이유&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정답이 &amp;ldquo;나는 강의를 듣는다&amp;rdquo;인데 예측이 &amp;ldquo;나는 나는 나는 나는&amp;rdquo;이라면 단순 Unigram Precision은 &amp;ldquo;나는&amp;rdquo;이 여러 번 맞았다고 과대평가할 수 있다. Clipping은 Reference에 등장한 최대 횟수까지만 일치로 인정한다. 따라서 반복으로 점수를 부풀리는 것을 막는다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Brevity Penalty가 필요한 이유&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;짧은 출력은 틀릴 기회가 적어 Precision이 높아지기 쉽다. Candidate 길이 &lt;code&gt;c&lt;/code&gt;가 Reference 길이 &lt;code&gt;r&lt;/code&gt;보다 짧으면 &lt;code&gt;BP = exp(1-r/c)&lt;/code&gt;를 곱해 벌점을 준다. 길이가 충분하면 &lt;code&gt;BP=1&lt;/code&gt;이다. BLEU는 n-gram Precision의 기하평균과 길이 벌점을 함께 봐야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Sentence BLEU와 Corpus BLEU&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;짧은 한 문장에서는 4-gram 일치가 0이 되어 전체 BLEU가 0이 되기 쉽다. 실제 평가는 여러 문장의 일치 수를 합친 Corpus BLEU를 주로 사용한다. 라이브러리마다 Tokenization, Smoothing, 대소문자 처리 설정이 다르면 점수가 달라질 수 있으므로 설정을 함께 기록해야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 전체 구조 다시 보기&lt;/h2&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;Source Tokens
 &amp;rarr; Embedding + Positional Encoding
 &amp;rarr; Encoder &amp;times; N
 &amp;rarr; Encoder Memory ─────────────────────┐
                                       &amp;darr;
Shifted Target &amp;rarr; Masked Self-Attention &amp;rarr; Cross-Attention &amp;rarr; FFN &amp;rarr; Logits&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강의의 Ablation 결과가 주는 메시지도 기억할 만하다. Head 수, 차원, Dropout, Label Smoothing 같은 선택이 성능에 영향을 주며, Learned Positional Embedding과 Sinusoidal Positional Encoding은 실험에서 큰 차이가 없었다. 구조 하나보다 여러 설계 요소의 균형이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Ablation Study&lt;/b&gt;는 모델의 구성 요소를 하나씩 바꾸거나 제거해 성능 변화를 확인한다. 전체 모델끼리의 단순 비교보다 어떤 선택이 실제 개선에 기여했는지 보여 준다. 표를 읽을 때는 Parameter Count, Training Cost, 평가 지표가 함께 변했는지 확인해야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 학습 Loss와 생성 품질은 같은가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cross-Entropy와 PPL은 정답 Token에 부여한 확률을 측정한다. 그러나 실제 생성에서는 Decoding Strategy가 개입하고, 하나의 입력에 여러 좋은 답이 있을 수 있다. BLEU도 표현이 다른 올바른 번역을 낮게 평가할 수 있다. 따라서 생성 모델 평가는 자동 지표만으로 끝내지 않고 Human Evaluation, 의미 기반 지표, Task-specific Metric을 함께 보는 것이 안전하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전체 흐름 점검&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Shifted Target
 &amp;rarr; Masked Self-Attention으로 Target Prefix 문맥화
 &amp;rarr; Cross-Attention으로 Encoder Memory 참조
 &amp;rarr; FFN과 Linear Projection으로 Vocabulary Logits 생성
 &amp;rarr; Cross-Entropy로 학습
 &amp;rarr; Autoregressive Decoding으로 추론
 &amp;rarr; PPL, BLEU 및 사람 평가로 품질 확인&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Key Terms&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Masked Self-Attention&lt;/code&gt; &amp;middot; &lt;code&gt;Causal Mask&lt;/code&gt; &amp;middot; &lt;code&gt;Cross-Attention&lt;/code&gt; &amp;middot; &lt;code&gt;Shifted Target&lt;/code&gt; &amp;middot; &lt;code&gt;Autoregressive Generation&lt;/code&gt; &amp;middot; &lt;code&gt;Greedy Decoding&lt;/code&gt; &amp;middot; &lt;code&gt;Beam Search&lt;/code&gt; &amp;middot; &lt;code&gt;Perplexity&lt;/code&gt; &amp;middot; &lt;code&gt;BLEU&lt;/code&gt; &amp;middot; &lt;code&gt;Brevity Penalty&lt;/code&gt; &amp;middot; &lt;code&gt;Clipped Precision&lt;/code&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Self Check&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Decoder 학습에서 정답 문장을 입력하면서도 미래 정보 누출을 막는 장치는 무엇인가?&lt;/li&gt;
&lt;li&gt;Cross-Attention의 Q, K, V는 각각 어디에서 오는가?&lt;/li&gt;
&lt;li&gt;PPL과 BLEU는 모델의 어떤 서로 다른 측면을 평가하는가?&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>DL/Transformer</category>
      <author>rem1004</author>
      <guid isPermaLink="true">https://rem1004.tistory.com/49</guid>
      <comments>https://rem1004.tistory.com/49#entry49comment</comments>
      <pubDate>Tue, 1 Sep 2026 03:27:41 +0900</pubDate>
    </item>
    <item>
      <title>3. Transformer의 입력 설계 - Token Embedding과 Positional Encoding</title>
      <link>https://rem1004.tistory.com/48</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Transformer는 RNN처럼 토큰을 한 개씩 읽지 않는다. 문장 전체를 동시에 처리한다. 병렬화에는 유리하지만, 순서를 알려 주는 장치가 없다면 &amp;ldquo;아니 다 오르는데 왜 테슬라만 떨어져?&amp;rdquo;와 &amp;ldquo;아니 테슬라 오르는데 왜 다 떨어져?&amp;rdquo;를 구분하기 어렵다. Transformer 입력은 &lt;b&gt;Token Embedding&lt;/b&gt;과 &lt;b&gt;Positional Information&lt;/b&gt;의 합으로 이 문제를 해결한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Token Embedding과 Positional Encoding을 더해 Transformer 입력을 만드는 과정&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;TTT-03-transformer-input.png&quot; data-origin-width=&quot;1774&quot; data-origin-height=&quot;887&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4yWYn/dJMcacRPjj5/YCrhNWIVbMpR0zMhCiMKCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4yWYn/dJMcacRPjj5/YCrhNWIVbMpR0zMhCiMKCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4yWYn/dJMcacRPjj5/YCrhNWIVbMpR0zMhCiMKCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4yWYn%2FdJMcacRPjj5%2FYCrhNWIVbMpR0zMhCiMKCK%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;1774&quot; height=&quot;887&quot; data-filename=&quot;TTT-03-transformer-input.png&quot; data-origin-width=&quot;1774&quot; data-origin-height=&quot;887&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. One-hot Vector에서 Token Embedding으로&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어휘 수가 7,851개라면 각 토큰은 길이 7,851의 &lt;b&gt;One-hot Vector&lt;/b&gt;로 표현할 수 있다. 하지만 대부분의 값이 0이고, 단어 사이의 의미적 유사성도 담지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Embedding Layer는 One-hot Vector를 더 작은 조밀 벡터인 &lt;b&gt;Token Embedding&lt;/b&gt;으로 바꾼다. 강의 예시에서는 &lt;code&gt;d_model = 512&lt;/code&gt;를 사용한다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;[batch, sequence_length, vocabulary_size]
                 &amp;darr; Embedding
[batch, sequence_length, d_model]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 토큰은 학습 가능한 512차원 벡터가 되고, 비슷한 맥락에서 쓰이는 단어는 학습을 통해 가까운 표현을 가질 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실제 입력 Tensor의 흐름&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문장을 바로 Embedding Layer에 넣는 것은 아니다. 먼저 &lt;b&gt;Tokenization&lt;/b&gt;으로 문장을 Token 단위로 나누고, Vocabulary에 정의된 Integer ID로 변환한다. Embedding Layer는 사실상 &lt;code&gt;vocabulary_size &amp;times; d_model&lt;/code&gt; 크기의 학습 가능한 Lookup Table이다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Raw Text
 &amp;rarr; Tokenization
 &amp;rarr; Token IDs: [batch, seq_len]
 &amp;rarr; Embedding Lookup
 &amp;rarr; Token Vectors: [batch, seq_len, d_model]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Token ID는 입력 단계에서 같은 벡터를 가져오지만, Encoder Layer를 통과한 뒤에는 주변 문맥에 따라 서로 다른 &lt;b&gt;Contextual Representation&lt;/b&gt;이 된다. 즉 Token Embedding은 문맥화 이전의 출발점이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 순서 정보가 사라지는 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Self-Attention은 입력 행렬의 모든 위치를 한꺼번에 계산한다. 별도의 위치 정보가 없다면 입력 토큰의 순서를 바꿔도 동일한 집합처럼 볼 위험이 있다. 이를 &lt;b&gt;Permutation Invariance&lt;/b&gt; 문제라고 이해할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 모델에는 &amp;ldquo;이 토큰은 첫 번째, 저 토큰은 여섯 번째&amp;rdquo;라는 정보가 필요하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Positional Embedding&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 직관적인 방법은 위치마다 학습 가능한 벡터를 두는 &lt;b&gt;Learned Positional Embedding&lt;/b&gt;이다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;Transformer Input = Token Embedding + Positional Embedding&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토큰 의미와 위치 벡터를 이어 붙이는 것이 아니라 더한다. 두 벡터의 차원이 같아야 하므로 위치 벡터도 &lt;code&gt;d_model&lt;/code&gt; 차원이다. 덧셈을 사용하면 입력 크기를 늘리지 않으면서 각 차원에 의미와 위치 정보를 함께 담을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Position 3의 토큰이 &lt;code&gt;apple&lt;/code&gt;이라면 최종 입력은 &lt;code&gt;Embedding(apple) + PositionVector(3)&lt;/code&gt;이다. 같은 &lt;code&gt;apple&lt;/code&gt;이 Position 10에 나오면 Token Embedding은 같지만 Position Vector가 달라져 서로 다른 입력 표현이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 학습형 위치 벡터는 훈련 때 정의한 최대 길이 밖의 위치를 자연스럽게 표현하기 어렵다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 주파수로 위치마다 고유한 패턴을 만드는 Sinusoidal Positional Encoding&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;TTT-03-sinusoidal-position.png&quot; data-origin-width=&quot;1921&quot; data-origin-height=&quot;819&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcShTk/dJMcaheGUjQ/6kbPozMlpAAdewLfOydm00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcShTk/dJMcaheGUjQ/6kbPozMlpAAdewLfOydm00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcShTk/dJMcaheGUjQ/6kbPozMlpAAdewLfOydm00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcShTk%2FdJMcaheGUjQ%2F6kbPozMlpAAdewLfOydm00%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;1921&quot; height=&quot;819&quot; data-filename=&quot;TTT-03-sinusoidal-position.png&quot; data-origin-width=&quot;1921&quot; data-origin-height=&quot;819&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. Sinusoidal Positional Encoding&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원 논문의 Transformer는 학습하지 않는 &lt;b&gt;Sinusoidal Positional Encoding&lt;/b&gt;을 사용했다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;PE(pos, 2i)   = sin(pos / 10000^(2i/d_model))
PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;짝수 차원에는 sine, 홀수 차원에는 cosine을 사용하고 차원마다 파장을 다르게 한다. 그 결과 각 위치는 고유한 패턴을 가지며, 모델은 상대적 거리도 학습하기 쉬워진다. 함수로 계산하므로 훈련보다 긴 위치에도 값을 만들 수 있다는 장점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;낮은 차원은 빠르게 진동해 가까운 위치 차이를 세밀하게 구분하고, 높은 차원은 천천히 변해 긴 범위의 위치 변화를 나타낸다. 여러 주파수를 합친 코드는 시계의 초침&amp;middot;분침&amp;middot;시침처럼 한 위치를 다중 Scale로 표현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sinusoidal Encoding이 상대 위치에 유리한 이유는 &lt;code&gt;PE(pos+k)&lt;/code&gt;를 &lt;code&gt;PE(pos)&lt;/code&gt;의 선형 변환으로 표현할 수 있기 때문이다. 모델은 Attention Projection을 학습하면서 절대 위치뿐 아니라 토큰 사이의 거리 패턴도 활용할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 꼭 구분해야 할 두 개념&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;Token Embedding&lt;/th&gt;
&lt;th&gt;Positional Encoding&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;담는 정보&lt;/td&gt;
&lt;td&gt;토큰의 의미와 용례&lt;/td&gt;
&lt;td&gt;문장 속 위치와 거리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;학습 여부&lt;/td&gt;
&lt;td&gt;보통 학습함&lt;/td&gt;
&lt;td&gt;학습형 또는 고정형&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;차원&lt;/td&gt;
&lt;td&gt;&lt;code&gt;d_model&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;d_model&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;결합 방식&lt;/td&gt;
&lt;td&gt;위치 표현과 더함&lt;/td&gt;
&lt;td&gt;토큰 표현과 더함&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. Padding과 Position을 함께 다루기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Batch 안에서 문장 길이를 맞추기 위해 &lt;code&gt;&amp;lt;pad&amp;gt;&lt;/code&gt; Token을 추가하면 Position Encoding도 그 위치에 더해질 수 있다. 그러나 Padding은 실제 문맥이 아니므로 Attention 계산에서 &lt;b&gt;Padding Mask&lt;/b&gt;를 적용해 다른 토큰이 &lt;code&gt;&amp;lt;pad&amp;gt;&lt;/code&gt;를 참고하지 못하게 해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Padding Mask와 Decoder의 Causal Mask는 목적이 다르다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Padding Mask&lt;/b&gt;: 존재하지 않는 토큰 위치를 가림&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Causal Mask&lt;/b&gt;: 아직 생성하지 않은 미래 위치를 가림&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 Mask가 동시에 필요한 Decoder 학습에서는 보통 결합해 Attention Score에 적용한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 입력 Scale과 Dropout&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원 Transformer는 Token Embedding에 &lt;code&gt;&amp;radic;d_model&lt;/code&gt;을 곱해 크기를 조정한 뒤 Positional Encoding을 더한다. 무작위 초기화된 Embedding의 값이 Position Encoding보다 지나치게 작아 의미 정보가 묻히는 것을 줄이기 위한 설계다. 합산 결과에는 Dropout을 적용해 특정 Token/Position 조합에 과적합하는 것을 완화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력 단계가 끝나면 각 토큰 벡터에는 &amp;ldquo;무슨 단어인가&amp;rdquo;와 &amp;ldquo;어디에 있는가&amp;rdquo;가 동시에 담긴다. 이제 이 벡터를 Query, Key, Value로 투영해 Self-Attention을 계산할 차례다.&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;&lt;b&gt;Positional Encoding이 문법을 직접 저장하는 것은 아니다.&lt;/b&gt; 위치 단서를 제공하고 문법적 관계는 Attention Layer가 학습한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;One-hot Vector를 실제로 거대한 행렬로 만들 필요는 없다.&lt;/b&gt; Token ID로 Embedding Table의 행을 바로 조회한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Position 정보는 한 번만 더해져도 깊은 Layer까지 전달된다.&lt;/b&gt; Residual Connection과 학습된 Projection을 통해 계속 활용된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;고정 Sinusoidal 방식만 Transformer의 정답은 아니다.&lt;/b&gt; Learned Absolute Position, Relative Position, Rotary Position Embedding 등 다양한 변형이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Key Terms&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;One-hot Vector&lt;/code&gt; &amp;middot; &lt;code&gt;Token Embedding&lt;/code&gt; &amp;middot; &lt;code&gt;Embedding Layer&lt;/code&gt; &amp;middot; &lt;code&gt;d_model&lt;/code&gt; &amp;middot; &lt;code&gt;Positional Embedding&lt;/code&gt; &amp;middot; &lt;code&gt;Positional Encoding&lt;/code&gt; &amp;middot; &lt;code&gt;Sinusoidal Encoding&lt;/code&gt; &amp;middot; &lt;code&gt;Permutation Invariance&lt;/code&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Self Check&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Token Embedding과 Positional Encoding을 더할 수 있는 조건은 무엇인가?&lt;/li&gt;
&lt;li&gt;Transformer에서 위치 정보가 별도로 필요한 이유는 무엇인가?&lt;/li&gt;
&lt;li&gt;Learned Positional Embedding과&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>DL/Transformer</category>
      <author>rem1004</author>
      <guid isPermaLink="true">https://rem1004.tistory.com/48</guid>
      <comments>https://rem1004.tistory.com/48#entry48comment</comments>
      <pubDate>Tue, 1 Sep 2026 03:24:38 +0900</pubDate>
    </item>
    <item>
      <title>2. Seq2Seq의 병목에서 Attention까지 - 왜 문장 전체를 한 벡터에 담으면 안 될까?</title>
      <link>https://rem1004.tistory.com/47</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;기계 번역은 입력 문장과 출력 문장의 길이가 다를 수 있다. 이를 위해 &lt;b&gt;Sequence-to-Sequence (Seq2Seq)&lt;/b&gt;는 입력을 읽는 &lt;b&gt;Encoder&lt;/b&gt;와 번역문을 생성하는 &lt;b&gt;Decoder&lt;/b&gt;를 분리했다. 좋은 출발이었지만, 긴 문장을 하나의 벡터로 압축한다는 결정이 곧 병목이 되었다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fixed Context의 정보 병목과 Attention의 Dynamic Context 비교&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;TTT-02-context-vs-attention.png&quot; data-origin-width=&quot;1774&quot; data-origin-height=&quot;887&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKaaRB/dJMcabeh5Cm/ijAlR5d8zfvKEoG3T0BK3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKaaRB/dJMcabeh5Cm/ijAlR5d8zfvKEoG3T0BK3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKaaRB/dJMcabeh5Cm/ijAlR5d8zfvKEoG3T0BK3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKaaRB%2FdJMcabeh5Cm%2FijAlR5d8zfvKEoG3T0BK3K%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;1774&quot; height=&quot;887&quot; data-filename=&quot;TTT-02-context-vs-attention.png&quot; data-origin-width=&quot;1774&quot; data-origin-height=&quot;887&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Seq2Seq의 기본 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Encoder RNN은 입력 토큰을 순서대로 읽고 마지막 Hidden State를 만든다. 이 벡터가 입력 문장 전체를 대표하는 &lt;b&gt;Context Vector&lt;/b&gt;다. Decoder RNN은 Context Vector를 초기 상태로 받아 &lt;code&gt;&amp;lt;sos&amp;gt;&lt;/code&gt;부터 한 단어씩 생성하고 &lt;code&gt;&amp;lt;eos&amp;gt;&lt;/code&gt;에서 멈춘다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;입력:  저는 / 강사 / 입니다
       └──── Encoder ────┘ &amp;rarr; Context Vector
                              &amp;darr;
출력: &amp;lt;sos&amp;gt; / I / am / an / instructor / &amp;lt;eos&amp;gt;
       └──────── Decoder ────────────────┘&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학습할 때는 이전 시점의 모델 예측 대신 실제 정답 토큰을 다음 입력으로 넣는 &lt;b&gt;Teacher Forcing&lt;/b&gt;을 자주 사용한다. 학습은 빠르고 안정적이지만, 추론 시에는 정답을 볼 수 없어 자신의 예측을 다시 입력해야 한다. 이 차이를 &lt;b&gt;Exposure Bias&lt;/b&gt;라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Teacher Forcing을 사용하면 초기 학습이 안정되고 각 시점의 Loss를 계산하기도 쉽다. 반면 추론 중에는 한 번 틀린 토큰이 다음 입력으로 들어가 오류가 연쇄적으로 커질 수 있다. 학습에서는 늘 정답 Prefix만 보았기 때문에 자신이 만든 잘못된 Prefix를 복구하는 법을 충분히 배우지 못했기 때문이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 하나의 Context Vector가 만드는 병목&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 Seq2Seq에서는 Encoder의 마지막 Hidden State 하나가 입력 전체를 떠맡는다. 문장이 길어지면 앞부분 정보가 소실되기 쉽다. 게다가 Encoder와 Decoder 모두 RNN이므로 긴 의존성과 직렬 계산 문제도 그대로다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Decoder가 특정 단어를 생성할 때 필요한 입력 부분을 골라 볼 수 없고, 이미 압축된 한 벡터에 의존해야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Attention의 아이디어&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Attention&lt;/b&gt;은 Decoder가 매 출력 시점마다 Encoder의 모든 Hidden State를 다시 참고하게 한다. 각 입력 위치가 현재 출력과 얼마나 관련 있는지 &lt;b&gt;Attention Score&lt;/b&gt;를 계산하고, 이를 확률처럼 정규화한 &lt;b&gt;Attention Weight&lt;/b&gt;로 가중합한다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Context_t = &amp;Sigma; AttentionWeight(t, i) &amp;times; EncoderHiddenState_i&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 번역문에서 &amp;ldquo;instructor&amp;rdquo;를 생성할 때 입력의 &amp;ldquo;강사&amp;rdquo; 위치에 큰 가중치를 줄 수 있다. 이제 Context Vector는 하나로 고정되지 않고 출력 시점마다 달라진다. 문장 전체를 한 병에 우겨 넣는 대신, 필요할 때 필요한 부분을 꺼내 보는 셈이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Score에서 Context까지의 계산&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Decoder 상태를 &lt;code&gt;s_t&lt;/code&gt;, Encoder의 각 상태를 &lt;code&gt;h_i&lt;/code&gt;라고 하자. 먼저 &lt;code&gt;e_(t,i)=score(s_t,h_i)&lt;/code&gt;로 관련도를 구한다. Score는 단순 내적, 학습 가능한 Bilinear Form, 작은 Neural Network 등으로 구현할 수 있다. 다음으로 입력 위치 방향 Softmax를 적용한다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;&amp;alpha;_(t,i) = exp(e_(t,i)) / &amp;Sigma;_j exp(e_(t,j))
c_t     = &amp;Sigma;_i &amp;alpha;_(t,i) h_i&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;&amp;alpha;_(t,i)&lt;/code&gt;의 합은 1이므로 &lt;code&gt;c_t&lt;/code&gt;는 Encoder 상태들의 가중 평균이다. Decoder는 &lt;code&gt;c_t&lt;/code&gt;와 자신의 상태를 함께 사용해 다음 토큰을 예측한다. Weight Matrix를 시각화하면 원문과 번역문 사이의 &lt;b&gt;Alignment&lt;/b&gt;를 관찰할 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Transformer의 Encoder Self-Attention, Masked Self-Attention, Cross-Attention&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;TTT-02-three-attention-paths.png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rjqmv/dJMcacdikHC/dpC7dkcKQAEcO2OGY8TKdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rjqmv/dJMcacdikHC/dpC7dkcKQAEcO2OGY8TKdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rjqmv/dJMcacdikHC/dpC7dkcKQAEcO2OGY8TKdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Frjqmv%2FdJMcacdikHC%2FdpC7dkcKQAEcO2OGY8TKdK%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;1536&quot; height=&quot;1024&quot; data-filename=&quot;TTT-02-three-attention-paths.png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. Self-Attention으로 가는 세 단계&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강의 자료는 Transformer로 이어지는 통찰을 세 가지로 정리한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Decoder가 Encoder의 모든 위치를 참고한다: &lt;b&gt;Encoder-Decoder Attention&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Decoder 내부에서도 이전 단어들 사이의 관계를 직접 본다: &lt;b&gt;Decoder Self-Attention&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Encoder에서도 입력 단어들끼리 서로를 직접 본다: &lt;b&gt;Encoder Self-Attention&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RNN에서는 멀리 떨어진 두 단어의 정보가 여러 Hidden State를 거쳐야 했다. Self-Attention에서는 두 위치가 한 번의 연산으로 직접 연결된다. 이 변화가 RNN을 완전히 제거한 Transformer의 토대다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. Attention이 해결한 것과 남긴 것&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Attention은 고정 Context Vector의 병목을 줄이고 해석 가능한 Alignment를 제공한다. 그러나 RNN 위에 Attention만 덧붙인 구조라면 여전히 시점별 순차 계산이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2017년의 논문 &lt;b&gt;Attention Is All You Need&lt;/b&gt;는 여기서 한 단계 더 나아갔다. recurrence와 convolution 없이 Attention만으로 Encoder-Decoder를 구성해 병렬 학습을 가능하게 했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. Attention이 장거리 관계에 유리한 이유와 비용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RNN에서 첫 번째 토큰 정보가 열 번째 토큰에 도달하려면 여러 Recurrent Step을 통과해야 한다. 경로가 길수록 정보와 Gradient가 약해질 수 있다. Self-Attention에서는 열 번째 Query가 첫 번째 Key를 직접 참고하므로 두 위치 사이의 계산 경로가 매우 짧다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 길이가 &lt;code&gt;n&lt;/code&gt;인 시퀀스의 모든 토큰 쌍을 비교하므로 기본 Self-Attention의 Score Matrix는 &lt;code&gt;n &amp;times; n&lt;/code&gt;이다. 시간과 메모리 복잡도는 대략 &lt;code&gt;O(n&amp;sup2;)&lt;/code&gt;이므로 매우 긴 문맥에서는 효율 문제가 생긴다. Attention은 모든 비용을 없앤 구조가 아니라, 순차 경로를 줄이는 대신 Pairwise Comparison 비용을 선택한 구조다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 세 종류의 Attention 구분&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;종류&lt;/th&gt;
&lt;th&gt;Query의 출처&lt;/th&gt;
&lt;th&gt;Key/Value의 출처&lt;/th&gt;
&lt;th&gt;목적&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Encoder Self-Attention&lt;/td&gt;
&lt;td&gt;Source Sequence&lt;/td&gt;
&lt;td&gt;Source Sequence&lt;/td&gt;
&lt;td&gt;입력 토큰끼리 문맥화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Decoder Masked Self-Attention&lt;/td&gt;
&lt;td&gt;Target Prefix&lt;/td&gt;
&lt;td&gt;Target Prefix&lt;/td&gt;
&lt;td&gt;미래를 보지 않고 출력 문맥화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Encoder-Decoder Attention&lt;/td&gt;
&lt;td&gt;Decoder&lt;/td&gt;
&lt;td&gt;Encoder Output&lt;/td&gt;
&lt;td&gt;출력이 입력의 관련 부분 참조&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;복습할 때 연결할 흐름&lt;/h2&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;고정 Context Vector의 정보 병목
  &amp;rarr; 모든 Encoder State를 보존
  &amp;rarr; Decoder State와 각 Encoder State의 Score 계산
  &amp;rarr; Softmax로 Attention Weight 생성
  &amp;rarr; Weighted Sum으로 시점별 Context 구성
  &amp;rarr; 같은 시퀀스 내부에도 적용한 Self-Attention
  &amp;rarr; RNN을 제거한 Transformer&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Key Terms&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Seq2Seq&lt;/code&gt; &amp;middot; &lt;code&gt;Encoder&lt;/code&gt; &amp;middot; &lt;code&gt;Decoder&lt;/code&gt; &amp;middot; &lt;code&gt;Context Vector&lt;/code&gt; &amp;middot; &lt;code&gt;Teacher Forcing&lt;/code&gt; &amp;middot; &lt;code&gt;Exposure Bias&lt;/code&gt; &amp;middot; &lt;code&gt;Attention Score&lt;/code&gt; &amp;middot; &lt;code&gt;Attention Weight&lt;/code&gt; &amp;middot; &lt;code&gt;Self-Attention&lt;/code&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Self Check&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;고정 Context Vector가 긴 문장에서 불리한 이유는 무엇인가?&lt;/li&gt;
&lt;li&gt;Attention의 Context Vector가 출력 시점마다 달라지는 이유는 무엇인가?&lt;/li&gt;
&lt;li&gt;RNN+Attention과 Transformer의 가장 큰 구조적 차이는 무엇인가?&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>DL/Transformer</category>
      <author>rem1004</author>
      <guid isPermaLink="true">https://rem1004.tistory.com/47</guid>
      <comments>https://rem1004.tistory.com/47#entry47comment</comments>
      <pubDate>Tue, 1 Sep 2026 03:21:49 +0900</pubDate>
    </item>
    <item>
      <title>1.  RNN은 순서를 어떻게 기억할까? - 순차 데이터에서 구조적 한계까지</title>
      <link>https://rem1004.tistory.com/46</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;문장을 숫자 벡터로 바꿨다고 해서 컴퓨터가 곧바로 문맥을 이해하는 것은 아니다. &amp;ldquo;저는 강사입니다&amp;rdquo;를 구성하는 단어는 서로 독립적이지 않고, 앞의 단어가 뒤의 단어 해석에 영향을 준다. 이런 &lt;b&gt;Sequential Data&lt;/b&gt;를 다루기 위해 등장한 대표 구조가 &lt;b&gt;Recurrent Neural Network (RNN)&lt;/b&gt;다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RNN이 Hidden State를 통해 순서 정보를 전달하는 구조&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;TTT-01-rnn-sequence-memory.png&quot; data-origin-width=&quot;1983&quot; data-origin-height=&quot;793&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cv27Tz/dJMb998Hb7r/bShSkKd6m61AqwYTPhP0Hk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cv27Tz/dJMb998Hb7r/bShSkKd6m61AqwYTPhP0Hk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cv27Tz/dJMb998Hb7r/bShSkKd6m61AqwYTPhP0Hk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcv27Tz%2FdJMb998Hb7r%2FbShSkKd6m61AqwYTPhP0Hk%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;1983&quot; height=&quot;793&quot; data-filename=&quot;TTT-01-rnn-sequence-memory.png&quot; data-origin-width=&quot;1983&quot; data-origin-height=&quot;793&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 왜 일반 MLP만으로는 순서를 다루기 어려울까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 단어를 &lt;b&gt;One-hot Encoding&lt;/b&gt;으로 표현하고 독립적인 &lt;b&gt;Multi-Layer Perceptron (MLP)&lt;/b&gt;에 넣으면, 모델은 현재 입력만 본다. &amp;ldquo;저는 &amp;rarr; 강사 &amp;rarr; 입니다&amp;rdquo;라는 순서가 사라지고 단어 세 개가 따로 처리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RNN의 핵심은 이전 시점의 정보가 다음 시점으로 이어지는 &lt;b&gt;Recurrent Connection&lt;/b&gt;이다. 현재 입력 &lt;code&gt;x_t&lt;/code&gt;뿐 아니라 이전의 &lt;b&gt;Hidden State&lt;/b&gt; &lt;code&gt;h_(t-1)&lt;/code&gt;까지 함께 사용한다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;h_t = tanh(x_t W_x + h_(t-1) W_h + b)
ŷ_t = h_t W_y + b_y&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;h_t&lt;/code&gt;는 지금까지 읽은 정보의 압축본이다. 같은 가중치 &lt;code&gt;W_x&lt;/code&gt;, &lt;code&gt;W_h&lt;/code&gt;, &lt;code&gt;W_y&lt;/code&gt;를 모든 시점에 공유하므로, 문장 길이가 달라져도 같은 규칙으로 처리할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;파라미터의 Shape까지 이해하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력 차원을 &lt;code&gt;d_x&lt;/code&gt;, Hidden State 차원을 &lt;code&gt;d_h&lt;/code&gt;, 출력 차원을 &lt;code&gt;d_y&lt;/code&gt;라고 하면 &lt;code&gt;W_x&lt;/code&gt;는 &lt;code&gt;d_x &amp;times; d_h&lt;/code&gt;, &lt;code&gt;W_h&lt;/code&gt;는 &lt;code&gt;d_h &amp;times; d_h&lt;/code&gt;, &lt;code&gt;W_y&lt;/code&gt;는 &lt;code&gt;d_h &amp;times; d_y&lt;/code&gt; Shape를 갖는다. 시퀀스가 길어져도 이 파라미터 수는 늘지 않는다. 시간축마다 새 Weight를 만드는 것이 아니라 같은 RNN Cell을 반복하기 때문이다. 다만 연산량과 저장해야 할 Activation은 시퀀스 길이에 비례해 늘어난다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 펼쳐 보면 보이는 RNN의 진짜 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RNN을 시간축으로 펼치는 것을 &lt;b&gt;Unrolling&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;pre class=&quot;llvm&quot;&gt;&lt;code&gt;x1 &amp;rarr; h1 &amp;rarr; h2 &amp;rarr; h3 &amp;rarr; h4
          &amp;uarr;    &amp;uarr;    &amp;uarr;
          x2   x3   x4&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;겉으로는 네트워크가 여러 개처럼 보이지만 실제로는 하나의 RNN Cell이 같은 파라미터를 공유하며 반복 실행된다. 이 때문에 RNN은 다음과 같은 형태를 모두 표현할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;One-to-Many&lt;/b&gt;: 하나의 입력에서 여러 출력 생성. 예: 이미지 설명 생성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Many-to-One&lt;/b&gt;: 여러 입력을 하나의 출력으로 요약. 예: 감성 분류&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Many-to-Many&lt;/b&gt;: 입력과 출력이 모두 시퀀스. 예: 기계 번역&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Many-to-Many도 구분해 두면 좋다. 품사 태깅처럼 각 입력 위치에 출력이 대응하는 구조와, 번역처럼 입력을 모두 읽은 뒤 다른 길이의 출력을 만드는 구조가 있다. 후자가 다음 편에서 다룰 Seq2Seq다. 실제 Batch 학습에서는 길이를 맞추기 위해 &lt;b&gt;Padding&lt;/b&gt;을 넣고, Padding 위치가 Loss나 상태 갱신에 영향을 주지 않도록 &lt;b&gt;Masking&lt;/b&gt;한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\&amp;mdash; BPTT와 Vanishing&amp;middot;Exploding Gradient의 관계&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;TTT-01-bptt-gradients.png&quot; data-origin-width=&quot;1774&quot; data-origin-height=&quot;887&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Azb8d/dJMcabZR4C7/yJi7Q9P4D0R3ckW8pS8fX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Azb8d/dJMcabZR4C7/yJi7Q9P4D0R3ckW8pS8fX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Azb8d/dJMcabZR4C7/yJi7Q9P4D0R3ckW8pS8fX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAzb8d%2FdJMcabZR4C7%2FyJi7Q9P4D0R3ckW8pS8fX1%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;1774&quot; height=&quot;887&quot; data-filename=&quot;TTT-01-bptt-gradients.png&quot; data-origin-width=&quot;1774&quot; data-origin-height=&quot;887&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 다음 토큰 예측은 어떻게 학습할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;H &amp;rarr; e &amp;rarr; l &amp;rarr; l &amp;rarr; o&amp;rdquo;를 학습한다고 하자. 각 시점에서 모델은 다음 글자를 맞히고, 예측 확률과 정답 사이의 &lt;b&gt;Cross-Entropy Loss&lt;/b&gt;를 계산한다. 이후 시간축 전체로 오차를 거꾸로 전달하는 &lt;b&gt;Backpropagation Through Time (BPTT)&lt;/b&gt;으로 가중치를 갱신한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 시퀀스가 길어질수록 미분값이 여러 번 곱해진다는 점이다. 값이 지나치게 작아지는 &lt;b&gt;Vanishing Gradient&lt;/b&gt;, 너무 커지는 &lt;b&gt;Exploding Gradient&lt;/b&gt;가 생길 수 있다. 특히 오래전 정보까지 학습하기 어렵다는 것이 기본 RNN의 구조적 약점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼 과거 &lt;code&gt;h_k&lt;/code&gt;가 현재 &lt;code&gt;h_t&lt;/code&gt;에 미치는 Gradient에는 &lt;code&gt;&amp;part;h_i/&amp;part;h_(i-1)&lt;/code&gt;가 시간 간격만큼 연속해서 곱해진다. 이 Jacobian의 크기가 반복해서 1보다 작으면 Gradient가 0에 가까워지고, 1보다 크면 폭발한다. Tanh의 미분값도 대부분 1보다 작으므로 긴 시간축에서 과거 정보가 약해지기 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Exploding Gradient는 &lt;b&gt;Gradient Clipping&lt;/b&gt;으로 완화할 수 있다. Gradient Norm이 임계값을 넘으면 크기를 줄이는 방식이다. 그러나 Clipping은 수치 폭발을 막을 뿐, 오래된 정보를 잘 기억하게 만드는 해결책은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Long Short-Term Memory (LSTM)&lt;/b&gt;와 &lt;b&gt;Gated Recurrent Unit (GRU)&lt;/b&gt;은 Gate를 이용해 정보의 보존과 삭제를 조절한다. 기본 RNN보다 장기 의존성을 잘 다루지만, 시간 순서대로 계산해야 하는 직렬성 자체는 남는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LSTM은 별도의 Cell State와 &lt;b&gt;Forget Gate&lt;/b&gt;, &lt;b&gt;Input Gate&lt;/b&gt;, &lt;b&gt;Output Gate&lt;/b&gt;를 사용한다. GRU는 Update Gate와 Reset Gate로 이를 단순화한다. 두 구조 모두 기억을 매 시점 완전히 덮어쓰지 않고 필요한 정보가 통과할 경로를 만든다는 공통점이 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. RNN의 핵심 한계&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RNN의 문제를 한 문장으로 정리하면 이렇다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 과거 정보를 하나의 Hidden State에 계속 압축하면서, 계산도 시간 순서대로 해야 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음 문제가 생긴다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;긴 문장의 앞부분 정보가 뒤로 갈수록 희미해진다.&lt;/li&gt;
&lt;li&gt;이전 시점 계산이 끝나야 다음 시점을 계산할 수 있어 &lt;b&gt;Parallelization&lt;/b&gt;이 어렵다.&lt;/li&gt;
&lt;li&gt;멀리 떨어진 단어 사이의 &lt;b&gt;Long-range Dependency&lt;/b&gt;를 직접 연결하기 어렵다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 한계를 해결하려는 다음 단계가 &lt;b&gt;Encoder-Decoder&lt;/b&gt;, 그리고 &lt;b&gt;Attention&lt;/b&gt;이다.&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;&lt;b&gt;Hidden State는 원문 저장소가 아니다.&lt;/b&gt; 지금까지의 입력을 고정 차원으로 압축한 학습된 표현이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Weight Sharing과 Hidden State Sharing은 다르다.&lt;/b&gt; Weight는 모든 시점에서 같지만 Hidden State는 시점마다 달라진다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가변 길이 처리와 병렬 처리는 다른 문제다.&lt;/b&gt; RNN은 길이가 다른 입력을 받을 수 있지만 시점 계산은 순서대로 진행된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LSTM도 장기 기억을 무한히 보장하지 않는다.&lt;/b&gt; Gradient 흐름은 좋아지지만 매우 긴 문맥과 직렬 계산 문제는 남는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개념 연결 지도&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Sequential Data
  &amp;rarr; Recurrent Connection
  &amp;rarr; Hidden State에 과거 정보 압축
  &amp;rarr; BPTT로 시간축 전체 학습
  &amp;rarr; Vanishing/Exploding Gradient
  &amp;rarr; LSTM&amp;middot;GRU로 기억 흐름 개선
  &amp;rarr; 병렬화와 장거리 의존성은 여전히 한계&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Key Terms&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Sequential Data&lt;/code&gt; &amp;middot; &lt;code&gt;RNN&lt;/code&gt; &amp;middot; &lt;code&gt;Recurrent Connection&lt;/code&gt; &amp;middot; &lt;code&gt;Hidden State&lt;/code&gt; &amp;middot; &lt;code&gt;Unrolling&lt;/code&gt; &amp;middot; &lt;code&gt;BPTT&lt;/code&gt; &amp;middot; &lt;code&gt;Vanishing Gradient&lt;/code&gt; &amp;middot; &lt;code&gt;Exploding Gradient&lt;/code&gt; &amp;middot; &lt;code&gt;LSTM&lt;/code&gt; &amp;middot; &lt;code&gt;GRU&lt;/code&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Self Check&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;h_t&lt;/code&gt;를 계산할 때 현재 입력 외에 무엇이 필요한가?&lt;/li&gt;
&lt;li&gt;RNN이 시점마다 서로 다른 가중치를 쓰지 않는 이유는 무엇인가?&lt;/li&gt;
&lt;li&gt;LSTM이 개선해도 완전히 없애지 못하는 RNN의 한계는 무엇인가?&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>DL/Transformer</category>
      <author>rem1004</author>
      <guid isPermaLink="true">https://rem1004.tistory.com/46</guid>
      <comments>https://rem1004.tistory.com/46#entry46comment</comments>
      <pubDate>Tue, 1 Sep 2026 03:17:14 +0900</pubDate>
    </item>
    <item>
      <title>5. MobileNet V1-V3와 EfficientNet - 정확도와 효율을 함께 설계하는 법</title>
      <link>https://rem1004.tistory.com/45</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;높은 정확도만큼 중요한 것이 연산량, 파라미터 수, 메모리, 실제 지연 시간이다. MobileNet 계열은 모바일 환경을 위한 효율적인 Convolution Block을 발전시켰고, EfficientNet은 모델의 Depth&amp;middot;Width&amp;middot;Resolution을 균형 있게 키우는 방법을 제안했다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Standard Convolution, Depthwise Separable Convolution, Inverted Residual 비교&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Legend13-05-mobile-blocks.png&quot; data-origin-width=&quot;1693&quot; data-origin-height=&quot;929&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMPlIj/dJMcaaNocT5/dRh2ARxfKaTSi4uG8nb1Yk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMPlIj/dJMcaaNocT5/dRh2ARxfKaTSi4uG8nb1Yk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMPlIj/dJMcaaNocT5/dRh2ARxfKaTSi4uG8nb1Yk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMPlIj%2FdJMcaaNocT5%2FdRh2ARxfKaTSi4uG8nb1Yk%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;1693&quot; height=&quot;929&quot; data-filename=&quot;Legend13-05-mobile-blocks.png&quot; data-origin-width=&quot;1693&quot; data-origin-height=&quot;929&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모바일 모델을 이해할 때는 &amp;ldquo;정확도를 조금 희생해 가볍게 만들었다&amp;rdquo;는 한 줄에서 멈추면 안 된다. 어떤 연산을 분리했고, 정보 손실을 어디서 막았으며, 실제 Hardware Latency를 어떻게 반영했는지가 핵심이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. MobileNet V1: Depthwise Separable Convolution&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 Convolution은 공간 특징 추출과 Channel 결합을 한 번에 한다. &lt;b&gt;MobileNet V1&lt;/b&gt;은 이를 둘로 분리한 &lt;b&gt;Depthwise Separable Convolution&lt;/b&gt;을 사용한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Depthwise Convolution&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Input Channel마다 하나의 공간 Kernel을 독립적으로 적용한다. Channel 사이를 섞지 않고 각 Channel의 공간 패턴만 찾는다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Pointwise Convolution&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 &lt;code&gt;1&amp;times;1 Convolution&lt;/code&gt;으로 Channel을 결합하고 Output Channel 수를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 &lt;code&gt;3&amp;times;3&lt;/code&gt; Convolution과 비교하면 연산량이 크게 줄지만 정확도 손실은 상대적으로 작다. 강의 자료의 비교처럼 파라미터 수와 Multiply-Add가 큰 폭으로 감소한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 Convolution의 계산량을 단순화하면 &lt;code&gt;D_k&amp;sup2; &amp;times; M &amp;times; N &amp;times; D_f&amp;sup2;&lt;/code&gt;다. Depthwise Separable 방식은 &lt;code&gt;D_k&amp;sup2; &amp;times; M &amp;times; D_f&amp;sup2; + M &amp;times; N &amp;times; D_f&amp;sup2;&lt;/code&gt;다. 여기서 &lt;code&gt;D_k&lt;/code&gt;는 Kernel Size, &lt;code&gt;M&lt;/code&gt;은 Input Channel, &lt;code&gt;N&lt;/code&gt;은 Output Channel, &lt;code&gt;D_f&lt;/code&gt;는 Feature Map 크기다. 두 방식의 비율은 대략 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1/N + 1/D_k&amp;sup2;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;3&amp;times;3&lt;/code&gt; Kernel과 큰 Output Channel에서는 일반 Convolution의 약 1/8~1/9 수준까지 계산을 줄일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MobileNet V1은 Downsampling Block에서 Stride 2를 사용하고, 별도의 큰 FC Layer 대신 GAP를 쓴다. 또한 두 Hyperparameter로 모델 크기를 조절한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Width Multiplier (&amp;alpha;)&lt;/b&gt;: 각 Layer의 Channel 수 조절&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Resolution Multiplier (&amp;rho;)&lt;/b&gt;: 입력 이미지 해상도 조절&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 &amp;alpha;와 &amp;rho;는 빠르고 가벼운 모델을 만들지만 정확도도 낮아질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Width Multiplier를 &lt;code&gt;&amp;alpha;&lt;/code&gt;로 줄이면 Input과 Output Channel이 함께 줄어 연산량이 대략 &lt;code&gt;&amp;alpha;&amp;sup2;&lt;/code&gt;에 비례해 감소한다. Resolution Multiplier &lt;code&gt;&amp;rho;&lt;/code&gt;는 Height와 Width를 함께 줄이므로 연산량이 대략 &lt;code&gt;&amp;rho;&amp;sup2;&lt;/code&gt;에 비례해 감소한다. 두 값은 정확도와 연산 예산을 연속적으로 조정할 수 있는 Knob이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. MobileNet V2: Inverted Residual과 Linear Bottleneck&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MobileNet V2는 &lt;b&gt;Inverted Residual Block&lt;/b&gt;을 도입한다. ResNet Bottleneck이 Channel을 줄였다가 복원한다면, V2는 좁은 입력을 먼저 넓히고 다시 줄인다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1&amp;times;1 Expansion &amp;rarr; 3&amp;times;3 Depthwise &amp;rarr; 1&amp;times;1 Projection&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Expansion: Channel을 &lt;code&gt;t&lt;/code&gt;배 확장&lt;/li&gt;
&lt;li&gt;Depthwise: 확장된 각 Channel의 공간 특징 추출&lt;/li&gt;
&lt;li&gt;Projection: 좁은 Output Channel로 압축&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stride가 1이고 Input/Output Shape가 같을 때만 Skip Connection을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 Projection에는 ReLU를 쓰지 않는 &lt;b&gt;Linear Bottleneck&lt;/b&gt;이 핵심이다. 낮은 차원의 Bottleneck에 ReLU를 적용하면 음수 영역이 모두 0이 되며 정보가 손실될 수 있다. 넓은 고차원 공간에서는 ReLU6를 사용하되, 좁은 출력에서는 Linear Activation으로 정보를 보존한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ReLU6는 출력을 0과 6 사이로 제한한다. 낮은 정밀도의 Quantization 환경에서 큰 Activation 값에 덜 민감하도록 설계된 선택이다. 그러나 좁은 Bottleneck에서는 어떤 차원이 사라지면 복원하기 어려우므로 마지막 Projection을 Linear하게 둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Skip Connection은 &lt;code&gt;stride=1&lt;/code&gt;이고 &lt;code&gt;C_in=C_out&lt;/code&gt;일 때만 사용할 수 있다. Stride 2로 Downsampling하거나 Channel 수가 다르면 Tensor Shape이 맞지 않아 Identity Addition을 생략한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. MobileNet V3: NAS, SE Block, Hard-Swish&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MobileNet V3는 &lt;b&gt;Neural Architecture Search (NAS)&lt;/b&gt;로 전체 구조를 탐색하고 수작업 조정을 더했다. Large와 Small 두 가지 모델을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요 변화는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;V2의 Inverted Residual 유지&lt;/li&gt;
&lt;li&gt;일부 Block에 &lt;b&gt;SE Block&lt;/b&gt; 추가&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Hard-Swish&lt;/b&gt; Activation 사용&lt;/li&gt;
&lt;li&gt;마지막 Stage와 Head의 연산 구조 개선&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swish는 &lt;code&gt;x &amp;times; sigmoid(x)&lt;/code&gt;이지만 모바일에서 Sigmoid 계산은 비쌀 수 있다. Hard-Swish는 이를 구간별 선형 함수로 근사한다.&lt;/p&gt;
&lt;pre class=&quot;gml&quot;&gt;&lt;code&gt;h-swish(x) = x &amp;times; ReLU6(x + 3) / 6&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SE Block에서도 Hard-Sigmoid를 사용해 Gate 계산을 효율화한다. 중요한 점은 파라미터 수만이 아니라 실제 하드웨어의 &lt;b&gt;Latency&lt;/b&gt;를 최적화 대상으로 삼았다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MobileNet V3는 초기와 마지막 Stage의 구조도 손봤다. 마지막 Expensive Layer 일부를 GAP 뒤로 이동하면 Spatial Size가 &lt;code&gt;1&amp;times;1&lt;/code&gt;인 상태에서 연산할 수 있어 비용이 크게 줄어든다. NAS가 찾은 구조를 그대로 쓰기보다 Hardware 특성과 구현 효율을 고려해 수정한 것이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Depth, Width, Resolution을 함께 확장하는 EfficientNet Compound Scaling&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Legend13-05-compound-scaling.png&quot; data-origin-width=&quot;1774&quot; data-origin-height=&quot;887&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WLjJd/dJMcajjfnXl/PGLSJVfvT99CySeGZqiIN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WLjJd/dJMcajjfnXl/PGLSJVfvT99CySeGZqiIN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WLjJd/dJMcajjfnXl/PGLSJVfvT99CySeGZqiIN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWLjJd%2FdJMcajjfnXl%2FPGLSJVfvT99CySeGZqiIN1%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;1774&quot; height=&quot;887&quot; data-filename=&quot;Legend13-05-compound-scaling.png&quot; data-origin-width=&quot;1774&quot; data-origin-height=&quot;887&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. EfficientNet: 한 축만 키우지 말자&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 Scaling 방식은 보통 한 요소만 키웠다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ResNet: &lt;b&gt;Depth Scaling&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;WideResNet: &lt;b&gt;Width Scaling&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;더 큰 입력: &lt;b&gt;Resolution Scaling&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 해상도가 커지면 더 넓은 Receptive Field와 더 많은 Channel Capacity도 필요하다. &lt;b&gt;EfficientNet&lt;/b&gt;은 세 축을 함께 키우는 &lt;b&gt;Compound Scaling&lt;/b&gt;을 제안한다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;depth      = &amp;alpha;^&amp;phi;
width      = &amp;beta;^&amp;phi;
resolution = &amp;gamma;^&amp;phi;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연산량이 대략 &lt;code&gt;depth &amp;times; width&amp;sup2; &amp;times; resolution&amp;sup2;&lt;/code&gt;에 비례하므로, 계수는 다음과 같은 제약 아래 정한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;&amp;alpha; &amp;times; &amp;beta;&amp;sup2; &amp;times; &amp;gamma;&amp;sup2; &amp;asymp; 2
&amp;alpha;, &amp;beta;, &amp;gamma; &amp;ge; 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 작은 Baseline &lt;b&gt;EfficientNet-B0&lt;/b&gt;에서 Grid Search로 균형 계수를 찾고, &lt;code&gt;&amp;phi;&lt;/code&gt;를 늘려 B1부터 B7까지 확장한다. 한 축만 과도하게 키우는 것보다 정확도 대비 FLOPs와 파라미터 효율이 좋아진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;&amp;phi;&lt;/code&gt;를 1 늘릴 때 계산량이 대략 2배가 되도록 &lt;code&gt;&amp;alpha;, &amp;beta;, &amp;gamma;&lt;/code&gt;를 고른다. 이렇게 한 번 찾은 비율을 유지하며 전체 Model Family를 생성한다. B1~B7은 완전히 별개의 Architecture Search 결과가 아니라 B0에 같은 Scaling Rule을 적용한 계열이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. EfficientNet-B0의 핵심 부품&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B0는 MobileNet V2 계열의 &lt;b&gt;MBConv (Mobile Inverted Bottleneck Convolution)&lt;/b&gt;를 사용하고 SE Block을 결합한다. Activation은 &lt;b&gt;SiLU/Swish&lt;/b&gt;, 정규화에는 Batch Normalization, 깊은 모델의 규제에는 &lt;b&gt;Stochastic Depth&lt;/b&gt;를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, EfficientNet은 완전히 새로운 Block 하나로 성공한 모델이 아니다. Mobile Inverted Bottleneck, SE, Swish 같은 검증된 요소를 효율적인 Baseline에 결합하고, Scaling 규칙을 체계화한 모델이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Stochastic Depth&lt;/b&gt;는 학습 중 일부 Residual Branch를 확률적으로 건너뛴다. Sample마다 더 얕은 Subnetwork를 학습하는 효과가 있어 깊은 모델의 Regularization을 돕는다. 추론 시에는 모든 Block을 사용하므로 구조가 결정적이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 효율을 비교할 때 생기는 함정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Parameter Count&lt;/b&gt;, &lt;b&gt;FLOPs&lt;/b&gt;, &lt;b&gt;Multiply-Adds&lt;/b&gt;, &lt;b&gt;Latency&lt;/b&gt;는 서로 같은 지표가 아니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파라미터가 적어도 메모리 접근이 비효율적이면 느릴 수 있다.&lt;/li&gt;
&lt;li&gt;FLOPs가 적어도 특정 하드웨어에서 Group/Depthwise 연산이 최적화되지 않으면 Latency가 높을 수 있다.&lt;/li&gt;
&lt;li&gt;입력 해상도와 Batch Size가 다르면 속도 비교가 공정하지 않다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 모바일 모델은 정확도뿐 아니라 실제 배포 장치의 Latency와 메모리까지 함께 측정해야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. Parameter와 FLOPs만으로 부족한 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Depthwise Convolution은 이론적 FLOPs가 매우 작지만 Arithmetic Intensity가 낮고 Memory Access 비중이 커 특정 Device에서는 기대만큼 빠르지 않을 수 있다. 반대로 표준 Convolution은 FLOPs가 많아도 GPU Kernel이 잘 최적화되어 실제 Latency가 더 나을 때가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공정한 Benchmark라면 다음 조건을 함께 기록해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Input Resolution과 Batch Size&lt;/li&gt;
&lt;li&gt;Precision: FP32, FP16, INT8&lt;/li&gt;
&lt;li&gt;Device와 Runtime Library&lt;/li&gt;
&lt;li&gt;Warm-up 여부와 반복 횟수&lt;/li&gt;
&lt;li&gt;End-to-end Latency인지 Kernel Latency인지&lt;/li&gt;
&lt;li&gt;Accuracy 측정 시 Crop과 Ensemble 사용 여부&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 모델 발전 흐름 한 줄 정리&lt;/h2&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;MobileNet V1: 공간 연산과 Channel 결합을 분리
MobileNet V2: 확장된 공간에서 처리하고 좁은 출력은 Linear하게 보존
MobileNet V3: NAS + SE + Hard-Swish로 실제 Latency 최적화
EfficientNet: 좋은 Block을 기반으로 Depth&amp;middot;Width&amp;middot;Resolution을 함께 Scaling&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;구조를 비교하며 외울 것&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;핵심 Block&lt;/th&gt;
&lt;th&gt;Scaling/최적화 축&lt;/th&gt;
&lt;th&gt;반드시 기억할 제한&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MobileNet V1&lt;/td&gt;
&lt;td&gt;Depthwise + Pointwise&lt;/td&gt;
&lt;td&gt;Width, Resolution&lt;/td&gt;
&lt;td&gt;Channel Mixing은 Pointwise가 담당&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MobileNet V2&lt;/td&gt;
&lt;td&gt;Inverted Residual&lt;/td&gt;
&lt;td&gt;Expansion Ratio&lt;/td&gt;
&lt;td&gt;좁은 Projection은 Linear&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MobileNet V3&lt;/td&gt;
&lt;td&gt;MBConv + SE&lt;/td&gt;
&lt;td&gt;NAS, Hardware Latency&lt;/td&gt;
&lt;td&gt;Hard-Swish/Hard-Sigmoid 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EfficientNet&lt;/td&gt;
&lt;td&gt;MBConv + SE&lt;/td&gt;
&lt;td&gt;Depth+Width+Resolution&lt;/td&gt;
&lt;td&gt;Compound Coefficient로 균형 확장&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Key Terms&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Depthwise Separable Convolution&lt;/code&gt; &amp;middot; &lt;code&gt;Depthwise Convolution&lt;/code&gt; &amp;middot; &lt;code&gt;Pointwise Convolution&lt;/code&gt; &amp;middot; &lt;code&gt;Width Multiplier&lt;/code&gt; &amp;middot; &lt;code&gt;Resolution Multiplier&lt;/code&gt; &amp;middot; &lt;code&gt;Inverted Residual&lt;/code&gt; &amp;middot; &lt;code&gt;Linear Bottleneck&lt;/code&gt; &amp;middot; &lt;code&gt;ReLU6&lt;/code&gt; &amp;middot; &lt;code&gt;Hard-Swish&lt;/code&gt; &amp;middot; &lt;code&gt;MBConv&lt;/code&gt; &amp;middot; &lt;code&gt;Compound Scaling&lt;/code&gt; &amp;middot; &lt;code&gt;Stochastic Depth&lt;/code&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Self Check&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Depthwise Convolution과 Pointwise Convolution은 각각 어떤 일을 하는가?&lt;/li&gt;
&lt;li&gt;MobileNet V2의 좁은 Projection Layer에서 ReLU를 제거한 이유는 무엇인가?&lt;/li&gt;
&lt;li&gt;EfficientNet이 Depth, Width, Resolution을 함께 키우는 이유는 무엇인가?&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>DL/CNN</category>
      <author>rem1004</author>
      <guid isPermaLink="true">https://rem1004.tistory.com/45</guid>
      <comments>https://rem1004.tistory.com/45#entry45comment</comments>
      <pubDate>Tue, 1 Sep 2026 03:10:09 +0900</pubDate>
    </item>
    <item>
      <title>4. WideResNet&amp;middot;ResNeXt&amp;middot;DenseNet&amp;middot;SE-Net - 깊이 다음의 네 가지 질문</title>
      <link>https://rem1004.tistory.com/44</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ResNet 이후 연구는 &amp;ldquo;무조건 더 깊게&amp;rdquo;라는 한 축에서 벗어나기 시작했다. 얼마나 넓게 만들 것인가, Branch 수를 얼마나 늘릴 것인가, 이전 Feature를 어떻게 재사용할 것인가, Channel별 중요도를 어떻게 조절할 것인가가 새로운 설계 축이 되었다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Width, Cardinality, Connectivity, Channel Attention의 네 가지 설계 축&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Legend13-04-four-design-axes.png&quot; data-origin-width=&quot;1983&quot; data-origin-height=&quot;793&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KPI2m/dJMcafVtIyf/UYOtoZQjtOTj2kyUUO2O5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KPI2m/dJMcafVtIyf/UYOtoZQjtOTj2kyUUO2O5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KPI2m/dJMcafVtIyf/UYOtoZQjtOTj2kyUUO2O5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKPI2m%2FdJMcafVtIyf%2FUYOtoZQjtOTj2kyUUO2O5k%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;1983&quot; height=&quot;793&quot; data-filename=&quot;Legend13-04-four-design-axes.png&quot; data-origin-width=&quot;1983&quot; data-origin-height=&quot;793&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 네 모델은 문제의식을 공유하지만 해결 방식은 다르다. WideResNet은 &lt;b&gt;Width&lt;/b&gt;, ResNeXt는 &lt;b&gt;Cardinality&lt;/b&gt;, DenseNet은 &lt;b&gt;Connectivity&lt;/b&gt;, SE-Net은 &lt;b&gt;Channel Recalibration&lt;/b&gt;을 중심 변수로 삼는다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. WideResNet: Depth보다 Width&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Wide Residual Network (WideResNet)&lt;/b&gt;은 ResNet을 지나치게 깊게 쌓으면 각 Residual Block의 기여가 작아지고 학습이 비효율적일 수 있다고 봤다. 대신 Layer 수를 줄이고 Channel 수, 즉 &lt;b&gt;Width&lt;/b&gt;를 키운다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델 표기 &lt;code&gt;WRN-d-k&lt;/code&gt;에서 &lt;code&gt;d&lt;/code&gt;는 Depth, &lt;code&gt;k&lt;/code&gt;는 &lt;b&gt;Widen Factor&lt;/b&gt;다. 예를 들어 &lt;code&gt;WRN-40-8&lt;/code&gt;은 40 Layer에 기본 Channel을 8배 넓힌 구조다. Residual Block 안에 Dropout을 넣어 넓어진 모델의 Overfitting을 완화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 교훈은 파라미터 예산이 같아도 Depth와 Width의 배분에 따라 정확도와 학습 시간이 달라진다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Width를 &lt;code&gt;k&lt;/code&gt;배 늘리면 Convolution Parameter와 FLOPs는 대략 &lt;code&gt;k&amp;sup2;&lt;/code&gt;에 비례해 증가한다. Feature Map의 Activation Memory도 커진다. WideResNet은 GPU가 넓은 Matrix 연산을 효율적으로 병렬 처리한다는 특성을 활용해 더 적은 Sequential Layer로 높은 성능을 얻는다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. ResNeXt: Cardinality라는 새 축&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ResNeXt&lt;/b&gt;는 Residual Bottleneck의 가운데 &lt;code&gt;3&amp;times;3 Convolution&lt;/code&gt;을 여러 Group으로 나눈 &lt;b&gt;Grouped Convolution&lt;/b&gt;으로 바꾼다. 각 Group이 일부 Channel만 처리하고 결과를 합친다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Branch 또는 Group의 수를 &lt;b&gt;Cardinality&lt;/b&gt;라고 한다. ResNeXt 논문은 Depth나 Width만 늘리는 것보다 Cardinality를 늘리는 것이 더 효율적인 경우가 있음을 보여 줬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Width&lt;/b&gt;는 한 변환이 사용하는 Channel 수이고 &lt;b&gt;Cardinality&lt;/b&gt;는 병렬 변환 또는 Group의 수다. 두 값은 독립적인 설계 축이다. Parameter Budget을 고정하면 Cardinality를 늘릴 때 Group당 Width를 함께 조정해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;표기 &lt;code&gt;32 &amp;times; 4d&lt;/code&gt;는 대략 32개의 Group과 각 Group의 폭 4를 뜻한다. PyTorch에서는 &lt;code&gt;groups=32&lt;/code&gt; 같은 설정으로 구현할 수 있다. Grouped Convolution 뒤에도 Skip Connection이 반드시 중요하다. Group 연산만으로 ResNeXt의 성능을 설명할 수는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 Convolution은 모든 Input Channel과 Output Channel을 연결한다. Grouped Convolution은 Channel을 &lt;code&gt;g&lt;/code&gt;개 Group으로 나눠 Group 내부에서만 연결하므로 같은 Input/Output Channel이라면 중앙 연산의 Parameter가 대략 &lt;code&gt;1/g&lt;/code&gt;로 줄어든다. ResNeXt는 절약한 예산을 더 많은 병렬 변환에 재배분한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. DenseNet: 더하는 대신 모두 이어 붙이기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ResNet이 &lt;code&gt;F(x) + x&lt;/code&gt;로 Feature를 더한다면 &lt;b&gt;DenseNet&lt;/b&gt;은 이전 모든 Layer의 Feature Map을 Channel 방향으로 &lt;b&gt;Concatenation&lt;/b&gt;한다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;x_l = H_l([x_0, x_1, ..., x_(l-1)])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Layer는 앞선 모든 표현에 직접 접근할 수 있어 &lt;b&gt;Feature Reuse&lt;/b&gt;가 강해지고 Gradient Path가 짧아진다. 새 Layer가 추가하는 Channel 수를 &lt;b&gt;Growth Rate (k)&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Input Channel이 &lt;code&gt;k_0&lt;/code&gt;이고 각 Layer가 &lt;code&gt;k&lt;/code&gt;개 Channel을 새로 만든다면 &lt;code&gt;l&lt;/code&gt;번째 Layer 입력 Channel은 &lt;code&gt;k_0 + k(l-1)&lt;/code&gt;이다. 각 Layer는 적은 수의 새 Feature만 만들고 기존 Feature를 다시 생성할 필요가 적다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Feature가 계속 Concatenate되면 Channel이 빠르게 늘어난다. 이를 제어하기 위해 DenseNet은 다음을 사용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Dense Block&lt;/b&gt;: 조밀 연결을 수행하는 구간&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Bottleneck Layer&lt;/b&gt;: &lt;code&gt;1&amp;times;1&lt;/code&gt;로 Channel을 줄인 뒤 &lt;code&gt;3&amp;times;3&lt;/code&gt; 적용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Transition Layer&lt;/b&gt;: Dense Block 사이에서 &lt;code&gt;1&amp;times;1 Convolution + Average Pooling&lt;/code&gt;으로 Channel과 해상도 축소&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Compression Factor (&amp;theta;)&lt;/b&gt;: Transition Layer에서 남길 Channel 비율&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DenseNet에도 BN과 ReLU가 Convolution보다 앞서는 Pre-activation 순서가 쓰인다. 다만 DenseNet은 Residual Addition이 아니라 Concatenation 기반이라는 점을 구분해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dense Connectivity는 Feature Reuse에 강하지만 모든 이전 Feature를 메모리에 유지하고 Concatenate해야 하므로 Memory Access 비용이 커질 수 있다. Parameter Count가 작다는 사실만으로 Training Memory와 Latency도 작다고 단정하면 안 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Squeeze, Excitation, Scale로 Channel Weight를 학습하는 SE Block&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Legend13-04-se-block.png&quot; data-origin-width=&quot;1881&quot; data-origin-height=&quot;836&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ssO6p/dJMcadC7SoI/4MTbBnJkVGPMCiakAETWTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ssO6p/dJMcadC7SoI/4MTbBnJkVGPMCiakAETWTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ssO6p/dJMcadC7SoI/4MTbBnJkVGPMCiakAETWTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FssO6p%2FdJMcadC7SoI%2F4MTbBnJkVGPMCiakAETWTK%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;1881&quot; height=&quot;836&quot; data-filename=&quot;Legend13-04-se-block.png&quot; data-origin-width=&quot;1881&quot; data-origin-height=&quot;836&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. SE-Net: Channel에 Attention 주기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Squeeze-and-Excitation Network (SE-Net)&lt;/b&gt;은 기존 CNN Block에 붙일 수 있는 가벼운 &lt;b&gt;SE Block&lt;/b&gt;을 제안했다. 각 Channel이 현재 이미지에 얼마나 중요한지 학습해 Feature Map을 재조정한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Squeeze&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Channel의 공간 정보를 &lt;b&gt;Global Average Pooling&lt;/b&gt;으로 하나의 값으로 압축한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;H &amp;times; W &amp;times; C &amp;rarr; 1 &amp;times; 1 &amp;times; C&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Excitation&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 FC Layer와 Activation을 통과시켜 Channel별 Gate를 만든다. 보통 중간 차원을 &lt;code&gt;C/r&lt;/code&gt;로 줄였다가 다시 &lt;code&gt;C&lt;/code&gt;로 복원한다. &lt;code&gt;r&lt;/code&gt;은 &lt;b&gt;Reduction Ratio&lt;/b&gt;다.&lt;/p&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;C &amp;rarr; C/r &amp;rarr; C &amp;rarr; Sigmoid&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Scale&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0과 1 사이의 Channel Weight를 원래 Feature Map에 곱한다. 중요한 Channel은 상대적으로 강조되고 덜 중요한 Channel은 억제된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SE의 Attention은 Transformer의 Token-to-Token Attention과 다르다. SE는 공간 위치를 GAP로 압축하고 Channel마다 하나의 Scale을 만든다. &amp;ldquo;어디를 볼지&amp;rdquo;보다 &amp;ldquo;어떤 Feature Channel을 얼마나 사용할지&amp;rdquo;를 조절하는 &lt;b&gt;Channel Attention&lt;/b&gt;이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. Ablation Study가 말해 주는 것&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SE-Net은 어느 Stage에 Block을 넣는지, Squeeze에 GAP 대신 다른 연산을 쓰는지, Excitation Activation과 Block 순서를 어떻게 바꾸는지 등을 &lt;b&gt;Ablation Study&lt;/b&gt;로 비교했다. 이런 실험은 &amp;ldquo;전체 모델이 좋아졌다&amp;rdquo;를 넘어 실제로 어느 설계가 개선에 기여했는지를 분리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SE Block은 파라미터와 연산을 조금 늘리지만 다양한 Backbone에 붙여 성능을 높였다. 이후 MobileNet V3와 EfficientNet에도 Channel Attention의 핵심 부품으로 이어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reduction Ratio &lt;code&gt;r&lt;/code&gt;이 너무 작으면 중간 FC가 커져 비용이 늘고, 너무 크면 Channel Dependency를 표현할 Capacity가 부족해진다. 강의 자료에서는 &lt;code&gt;r=16&lt;/code&gt;이 좋은 균형으로 제시되지만 최적값은 Backbone과 Channel 수에 따라 달라질 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 네 모델을 한눈에 비교하기&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;핵심 질문&lt;/th&gt;
&lt;th&gt;연결/연산 방식&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;WideResNet&lt;/td&gt;
&lt;td&gt;더 깊게 말고 더 넓게 하면?&lt;/td&gt;
&lt;td&gt;Wider Residual Block&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ResNeXt&lt;/td&gt;
&lt;td&gt;병렬 변환 수를 늘리면?&lt;/td&gt;
&lt;td&gt;Grouped Convolution, Cardinality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DenseNet&lt;/td&gt;
&lt;td&gt;이전 Feature를 모두 재사용하면?&lt;/td&gt;
&lt;td&gt;Dense Concatenation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SE-Net&lt;/td&gt;
&lt;td&gt;중요한 Channel을 고를 수 있다면?&lt;/td&gt;
&lt;td&gt;Squeeze, Excitation, Scale&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 연결 방식을 Tensor Shape으로 비교하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Input과 Branch Output이 모두 &lt;code&gt;H&amp;times;W&amp;times;C&lt;/code&gt;라고 하자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Residual Addition&lt;/b&gt;: 결과도 &lt;code&gt;H&amp;times;W&amp;times;C&lt;/code&gt;, Channel 수 유지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Dense Concatenation&lt;/b&gt;: 결과는 &lt;code&gt;H&amp;times;W&amp;times;2C&lt;/code&gt;, Channel 수 증가&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Grouped Convolution&lt;/b&gt;: 결과는 설정한 &lt;code&gt;C_out&lt;/code&gt;, 내부 연결만 Group으로 제한&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SE Scaling&lt;/b&gt;: 결과는 &lt;code&gt;H&amp;times;W&amp;times;C&lt;/code&gt;, 값만 Channel별 재가중&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 무엇을 기준으로 선택할까?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;깊은 Sequential 연산이 병목이면 Width를 늘린 Block을 검토한다.&lt;/li&gt;
&lt;li&gt;같은 예산에서 다양한 변환을 원하면 Cardinality를 본다.&lt;/li&gt;
&lt;li&gt;Feature Reuse와 짧은 Gradient Path가 중요하면 Dense Connectivity를 고려한다.&lt;/li&gt;
&lt;li&gt;기존 Backbone에 Channel 선택 능력을 추가하려면 SE Block이 간단하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택은 Parameter Count 하나로 결정하지 않는다. FLOPs, Activation Memory, Memory Access, Batch Size, Target Hardware Latency를 함께 측정해야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;발전 축 정리&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ResNet의 Skip Connection
 ├─ Width 확장         &amp;rarr; WideResNet
 ├─ Cardinality 확장   &amp;rarr; ResNeXt
 ├─ Dense Connectivity &amp;rarr; DenseNet
 └─ Channel Attention  &amp;rarr; SE-Net&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Key Terms&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Width&lt;/code&gt; &amp;middot; &lt;code&gt;Widen Factor&lt;/code&gt; &amp;middot; &lt;code&gt;Grouped Convolution&lt;/code&gt; &amp;middot; &lt;code&gt;Cardinality&lt;/code&gt; &amp;middot; &lt;code&gt;Dense Connectivity&lt;/code&gt; &amp;middot; &lt;code&gt;Feature Reuse&lt;/code&gt; &amp;middot; &lt;code&gt;Growth Rate&lt;/code&gt; &amp;middot; &lt;code&gt;Transition Layer&lt;/code&gt; &amp;middot; &lt;code&gt;Squeeze-and-Excitation&lt;/code&gt; &amp;middot; &lt;code&gt;Reduction Ratio&lt;/code&gt; &amp;middot; &lt;code&gt;Ablation Study&lt;/code&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Self Check&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Width와 Cardinality는 어떻게 다른가?&lt;/li&gt;
&lt;li&gt;ResNet의 Addition과 DenseNet의 Concatenation은 Feature 수에 어떤 차이를 만드는가?&lt;/li&gt;
&lt;li&gt;SE Block의 Squeeze, Excitation, Scale을 순서대로 설명할 수 있는가?&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>DL/CNN</category>
      <author>rem1004</author>
      <guid isPermaLink="true">https://rem1004.tistory.com/44</guid>
      <comments>https://rem1004.tistory.com/44#entry44comment</comments>
      <pubDate>Tue, 1 Sep 2026 03:03:36 +0900</pubDate>
    </item>
    <item>
      <title>2. Inception Net V1-V3 - 여러 크기의 특징을 한 번에 보는 네트워크</title>
      <link>https://rem1004.tistory.com/43</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 속 대상은 크기가 제각각이다. 어떤 패턴은 &lt;code&gt;1&amp;times;1&lt;/code&gt;, 어떤 패턴은 &lt;code&gt;3&amp;times;3&lt;/code&gt;, 또 어떤 패턴은 &lt;code&gt;5&amp;times;5&lt;/code&gt; 범위에서 잘 포착된다. &lt;b&gt;Inception Network&lt;/b&gt;는 어느 Kernel 크기가 정답인지 하나만 고르지 않고, 여러 연산을 병렬로 수행한 뒤 결과를 합친다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1&amp;times;1 Bottleneck과 Multi-scale Branch로 구성된 Inception Module&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Legend13-02-inception-module.png&quot; data-origin-width=&quot;1774&quot; data-origin-height=&quot;887&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSmfPf/dJMcaazQ4W3/HZGblarCjNu7QwqZcBAPTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSmfPf/dJMcaazQ4W3/HZGblarCjNu7QwqZcBAPTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSmfPf/dJMcaazQ4W3/HZGblarCjNu7QwqZcBAPTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSmfPf%2FdJMcaazQ4W3%2FHZGblarCjNu7QwqZcBAPTk%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;1774&quot; height=&quot;887&quot; data-filename=&quot;Legend13-02-inception-module.png&quot; data-origin-width=&quot;1774&quot; data-origin-height=&quot;887&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Inception Module의 출발점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 아이디어는 다음 Branch를 같은 입력에 병렬 적용하는 것이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;1&amp;times;1 Convolution&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;3&amp;times;3 Convolution&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;5&amp;times;5 Convolution&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;3&amp;times;3 Max Pooling&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Branch의 출력은 공간 크기를 같게 맞춘 뒤 Channel 방향으로 &lt;b&gt;Concatenation&lt;/b&gt;한다. 서로 다른 Receptive Field에서 얻은 특징을 다음 Layer가 함께 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 큰 Kernel을 많은 Channel에 그대로 적용하면 계산량이 너무 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Concatenation을 하려면 모든 Branch의 Height와 Width가 같아야 한다. 그래서 &lt;code&gt;3&amp;times;3&lt;/code&gt;, &lt;code&gt;5&amp;times;5&lt;/code&gt; Branch에는 알맞은 Padding을 사용하고, Pooling Branch도 Stride 1과 Padding을 적용한다. Spatial Shape은 유지하고 Channel만 합친다는 원칙이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 1&amp;times;1 Convolution이라는 Bottleneck&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Inception Net V1 (GoogLeNet)&lt;/b&gt;은 비싼 &lt;code&gt;3&amp;times;3&lt;/code&gt;, &lt;code&gt;5&amp;times;5&lt;/code&gt; 앞에 &lt;code&gt;1&amp;times;1 Convolution&lt;/code&gt;을 두어 Channel 수를 줄였다. 이 구조를 &lt;b&gt;Bottleneck&lt;/b&gt; 또는 &lt;b&gt;Dimension Reduction&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 192 Channel을 곧바로 128개의 &lt;code&gt;3&amp;times;3&lt;/code&gt; Kernel에 넣는 대신, 먼저 &lt;code&gt;1&amp;times;1&lt;/code&gt;로 96 Channel까지 줄인 뒤 &lt;code&gt;3&amp;times;3&lt;/code&gt;을 적용하면 연산량과 파라미터가 크게 감소한다. &lt;code&gt;1&amp;times;1&lt;/code&gt; 뒤에 Activation도 들어가므로 단순 압축 이상의 비선형 표현을 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자로 비교하면 직접 &lt;code&gt;3&amp;times;3&lt;/code&gt;을 적용할 때 Weight 수는 &lt;code&gt;3&amp;times;3&amp;times;192&amp;times;128 = 221,184&lt;/code&gt;다. &lt;code&gt;1&amp;times;1&lt;/code&gt;로 96 Channel까지 줄이면 &lt;code&gt;1&amp;times;1&amp;times;192&amp;times;96 + 3&amp;times;3&amp;times;96&amp;times;128 = 129,024&lt;/code&gt;개다. Bottleneck에도 비용이 있지만 전체는 약 42% 줄어든다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Inception V1의 전체 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GoogLeNet은 Inception Module을 깊게 쌓고 마지막에 &lt;b&gt;Global Average Pooling (GAP)&lt;/b&gt;을 사용해 거대한 FC Layer를 없앴다. VGGNet보다 훨씬 적은 파라미터로 높은 성능을 냈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;깊은 네트워크 중간에는 &lt;b&gt;Auxiliary Classifier&lt;/b&gt;를 달았다. 중간 Feature Map에서도 분류 Loss를 계산해 초기 Layer까지 Gradient가 잘 흐르도록 돕는 장치다. 추론 때는 보조 분류기를 사용하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성능 평가에서 여러 Crop과 여러 모델 결과를 합치는 &lt;b&gt;Ensemble&lt;/b&gt;을 사용했다는 점도 주의해야 한다. 단일 모델 성능과 Ensemble 성능은 공정하게 구분해 비교해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Auxiliary Classifier의 Loss는 Main Loss에 일정 Weight를 곱해 더한다. 이는 학습 중 깊은 중간 Layer에 직접 감독 신호를 주는 &lt;b&gt;Deep Supervision&lt;/b&gt;이다. 추론 시 제거되므로 최종 Latency에는 영향을 주지 않지만 학습 그래프와 Loss 구성에는 포함된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;큰 Kernel을 작은 Kernel로 분해하고 효율적으로 Downsampling하는 Inception 설계&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Legend13-02-factorization.png&quot; data-origin-width=&quot;1983&quot; data-origin-height=&quot;793&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9n7Sm/dJMcajjflak/feI0tvGhbfhKN3L72F3T1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9n7Sm/dJMcajjflak/feI0tvGhbfhKN3L72F3T1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9n7Sm/dJMcajjflak/feI0tvGhbfhKN3L72F3T1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9n7Sm%2FdJMcajjflak%2FfeI0tvGhbfhKN3L72F3T1k%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;1983&quot; height=&quot;793&quot; data-filename=&quot;Legend13-02-factorization.png&quot; data-origin-width=&quot;1983&quot; data-origin-height=&quot;793&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. Inception V2/V3의 Factorization&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;V2와 V3는 큰 Convolution을 더 작은 연산으로 분해하는 &lt;b&gt;Factorization&lt;/b&gt;을 적극 사용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;5&amp;times;5&lt;/code&gt; &amp;rarr; &lt;code&gt;3&amp;times;3&lt;/code&gt; 두 번&lt;/li&gt;
&lt;li&gt;&lt;code&gt;3&amp;times;3&lt;/code&gt; &amp;rarr; &lt;code&gt;1&amp;times;3&lt;/code&gt; 다음 &lt;code&gt;3&amp;times;1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같거나 비슷한 Receptive Field를 유지하면서 파라미터와 연산을 줄이고 Activation 횟수는 늘린다. 다만 너무 이른 Layer에서 지나치게 분해하면 공간 구조를 충분히 잡지 못할 수 있어 해상도에 따라 모듈 구성을 다르게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;3&amp;times;3&lt;/code&gt;을 &lt;code&gt;1&amp;times;3&lt;/code&gt;과 &lt;code&gt;3&amp;times;1&lt;/code&gt;로 나누면 Kernel당 공간 Weight가 9개에서 6개로 줄어든다. 이를 &lt;b&gt;Asymmetric Convolution&lt;/b&gt;이라고 한다. 사각형 영역을 세로&amp;middot;가로 두 단계로 처리하면서 같은 Receptive Field를 확보하고 중간 Activation도 추가한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 효율적인 Grid Reduction&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pooling만으로 Downsampling하면 표현 공간이 갑자기 좁아지는 &lt;b&gt;Representational Bottleneck&lt;/b&gt;이 생길 수 있다. Inception V3는 Stride 2 Convolution Branch와 Pooling Branch를 병렬로 수행한 뒤 Concatenate한다. 공간 해상도를 줄이면서 Channel 표현을 더 풍부하게 유지하는 방식이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. Batch Normalization과 Label Smoothing&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;V2/V3 계열은 &lt;b&gt;Batch Normalization (BN)&lt;/b&gt;을 널리 사용해 학습을 안정화한다. 또한 &lt;b&gt;Label Smoothing&lt;/b&gt;을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;One-hot Label이 정답 클래스에 확률 1, 나머지에 0을 준다면 Label Smoothing은 정답 확률을 조금 낮추고 나머지 클래스에 작은 확률을 배분한다. 모델이 지나치게 확신하는 것을 막고 일반화를 돕는다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;smoothed label = (1 - &amp;epsilon;) &amp;times; one_hot + &amp;epsilon; / K&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;K&lt;/code&gt;는 클래스 수, &lt;code&gt;&amp;epsilon;&lt;/code&gt;는 Smoothing 정도다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Label Smoothing은 정답 Class와 다른 Class 사이의 Logit 간격이 끝없이 커지는 것을 억제한다. Calibration과 일반화에 도움이 될 수 있지만, Teacher의 Dark Knowledge를 활용하는 Knowledge Distillation이나 정확한 Confidence가 중요한 문제에서는 Smoothing 강도를 검토해야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. Inception이 남긴 설계 원칙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Inception의 진짜 유산은 특정 Module 모양보다 다음 세 가지다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;여러 Scale의 특징을 병렬로 본다.&lt;/li&gt;
&lt;li&gt;비싼 연산 전에 Bottleneck으로 Channel을 조절한다.&lt;/li&gt;
&lt;li&gt;큰 Kernel을 작은 Kernel이나 비대칭 Kernel로 Factorization한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. Inception V1, V2, V3의 차이&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;중심 개선&lt;/th&gt;
&lt;th&gt;기억할 요소&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;V1&lt;/td&gt;
&lt;td&gt;Multi-scale Branch와 계산 절감&lt;/td&gt;
&lt;td&gt;1&amp;times;1 Bottleneck, GAP, Auxiliary Classifier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;V2&lt;/td&gt;
&lt;td&gt;큰 Kernel의 효율적 분해&lt;/td&gt;
&lt;td&gt;5&amp;times;5 &amp;rarr; 3&amp;times;3 + 3&amp;times;3, Batch Normalization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;V3&lt;/td&gt;
&lt;td&gt;Factorization과 학습 기법 정교화&lt;/td&gt;
&lt;td&gt;Asymmetric Conv, Grid Reduction, Label Smoothing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;논문이나 구현체에 따라 V2/V3 명칭과 세부 구분이 다소 혼용되기도 한다. 이름만 외우기보다 &amp;ldquo;Bottleneck &amp;rarr; Factorization &amp;rarr; 효율적 Downsampling &amp;rarr; Regularization&amp;rdquo;이라는 발전 방향을 이해하는 편이 안전하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. Inception 구조를 구현할 때의 함정&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Branch마다 Spatial Shape이 달라지면 Concatenation할 수 없다.&lt;/li&gt;
&lt;li&gt;Concatenation 뒤 Channel 수는 각 Branch Output Channel의 합이다.&lt;/li&gt;
&lt;li&gt;Bottleneck Channel을 너무 줄이면 연산은 가벼워지지만 정보 병목이 심해진다.&lt;/li&gt;
&lt;li&gt;Auxiliary Loss의 Weight를 너무 크게 두면 중간 분류 목표가 Main Task를 방해할 수 있다.&lt;/li&gt;
&lt;li&gt;여러 Crop&amp;middot;Ensemble 성능을 단일 Forward 성능과 혼동하면 안 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;모델을 관통하는 설계 흐름&lt;/h2&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;여러 Scale의 특징이 필요함
 &amp;rarr; 여러 Branch를 병렬 실행
 &amp;rarr; 계산량 증가
 &amp;rarr; 1&amp;times;1 Bottleneck으로 Channel 축소
 &amp;rarr; 큰 Kernel을 작은/비대칭 Kernel로 Factorization
 &amp;rarr; Downsampling에서도 여러 Branch를 유지
 &amp;rarr; BN&amp;middot;Label Smoothing으로 학습 안정화&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Key Terms&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Inception Module&lt;/code&gt; &amp;middot; &lt;code&gt;Multi-scale Feature&lt;/code&gt; &amp;middot; &lt;code&gt;Bottleneck&lt;/code&gt; &amp;middot; &lt;code&gt;Dimension Reduction&lt;/code&gt; &amp;middot; &lt;code&gt;Concatenation&lt;/code&gt; &amp;middot; &lt;code&gt;Auxiliary Classifier&lt;/code&gt; &amp;middot; &lt;code&gt;Factorization&lt;/code&gt; &amp;middot; &lt;code&gt;Grid Reduction&lt;/code&gt; &amp;middot; &lt;code&gt;Batch Normalization&lt;/code&gt; &amp;middot; &lt;code&gt;Label Smoothing&lt;/code&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Self Check&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Inception Module에서 Branch 출력을 더하지 않고 Concatenate하는 이유는 무엇인가?&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1&amp;times;1 Convolution&lt;/code&gt;이 연산량을 줄이는 과정을 설명할 수 있는가?&lt;/li&gt;
&lt;li&gt;Label Smoothing은 모델의 어떤 행동을 완화하는가?&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>DL/CNN</category>
      <author>rem1004</author>
      <guid isPermaLink="true">https://rem1004.tistory.com/43</guid>
      <comments>https://rem1004.tistory.com/43#entry43comment</comments>
      <pubDate>Tue, 1 Sep 2026 02:55:13 +0900</pubDate>
    </item>
  </channel>
</rss>