<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>지노랩 /JinoLab</title>
    <link>https://jinolab.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Tue, 23 Jun 2026 19:19:54 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>지노랩/JinoLab</managingEditor>
    <image>
      <title>지노랩 /JinoLab</title>
      <url>https://tistory1.daumcdn.net/tistory/5869123/attach/df6982d065464a0498779476a8278c55</url>
      <link>https://jinolab.tistory.com</link>
    </image>
    <item>
      <title>21. Deep Learning을 위한 미분(calculus)</title>
      <link>https://jinolab.tistory.com/363</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 미분이 뭐예요?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수 &lt;span&gt;f(x)&lt;/span&gt;의 &lt;b&gt;미분&lt;/b&gt;은 &amp;ldquo;함수 값이 얼마나 빨리 변하는지&amp;rdquo;를 나타내는 수학적 도구예요.&lt;/li&gt;
&lt;li&gt;기하학적으로는 &lt;b&gt;접선의 기울기&lt;/b&gt;를 뜻해요.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;275&quot; data-origin-height=&quot;62&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfcvOy/btsPs0iXaz4/jkvgMajrLv4nonWrAm8Hu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfcvOy/btsPs0iXaz4/jkvgMajrLv4nonWrAm8Hu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfcvOy/btsPs0iXaz4/jkvgMajrLv4nonWrAm8Hu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfcvOy%2FbtsPs0iXaz4%2FjkvgMajrLv4nonWrAm8Hu0%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;275&quot; height=&quot;62&quot; data-origin-width=&quot;275&quot; data-origin-height=&quot;62&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;즉, &lt;span&gt;x&lt;/span&gt;를 아주 조금 &lt;span&gt;&amp;Delta;x &lt;/span&gt;만큼 바꿨을 때, &lt;span&gt;f&lt;/span&gt;가 얼마나 변하는지( &lt;span&gt;&amp;Delta;f&lt;/span&gt;&amp;nbsp;)를 &lt;span&gt;&amp;Delta;x&lt;/span&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;2. 주요 미분 공식&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;상수배 법칙&lt;/b&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;177&quot; data-origin-height=&quot;52&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/djKOpl/btsPsD9IE7O/kNQ1ueXyTSGaHzk5cJfSGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/djKOpl/btsPsD9IE7O/kNQ1ueXyTSGaHzk5cJfSGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/djKOpl/btsPsD9IE7O/kNQ1ueXyTSGaHzk5cJfSGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdjKOpl%2FbtsPsD9IE7O%2FkNQ1ueXyTSGaHzk5cJfSGk%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;177&quot; height=&quot;52&quot; data-origin-width=&quot;177&quot; data-origin-height=&quot;52&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상수 &lt;span&gt;cc&lt;/span&gt;는 그대로 밖으로 나온다!&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;거듭제곱 법칙 (Power Rule)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;439&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNw9Ct/btsPsGk0Ayj/cVKL13YDYlq9bdmoByKGL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNw9Ct/btsPsGk0Ayj/cVKL13YDYlq9bdmoByKGL1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNw9Ct/btsPsGk0Ayj/cVKL13YDYlq9bdmoByKGL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNw9Ct%2FbtsPsGk0Ayj%2FcVKL13YDYlq9bdmoByKGL1%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;439&quot; height=&quot;96&quot; data-origin-width=&quot;439&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;합의 법칙&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;276&quot; data-origin-height=&quot;51&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTFmgy/btsPt9F537U/o9MksxklfIU8L9q2svkpoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTFmgy/btsPt9F537U/o9MksxklfIU8L9q2svkpoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTFmgy/btsPt9F537U/o9MksxklfIU8L9q2svkpoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTFmgy%2FbtsPt9F537U%2Fo9MksxklfIU8L9q2svkpoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;276&quot; height=&quot;51&quot; data-origin-width=&quot;276&quot; data-origin-height=&quot;51&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;차의 법칙&lt;/b&gt; (합과 같음)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;275&quot; data-origin-height=&quot;51&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Dwr8D/btsPsR7FRQH/6JKXTWFvDPvbomXvfyEPv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Dwr8D/btsPsR7FRQH/6JKXTWFvDPvbomXvfyEPv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Dwr8D/btsPsR7FRQH/6JKXTWFvDPvbomXvfyEPv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDwr8D%2FbtsPsR7FRQH%2F6JKXTWFvDPvbomXvfyEPv0%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;275&quot; height=&quot;51&quot; data-origin-width=&quot;275&quot; data-origin-height=&quot;51&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;곱의 법칙 (Product Rule)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;347&quot; data-origin-height=&quot;48&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nQWdH/btsPuFkjHaB/izjRH4RmbfcpUkFGSWWtC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nQWdH/btsPuFkjHaB/izjRH4RmbfcpUkFGSWWtC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nQWdH/btsPuFkjHaB/izjRH4RmbfcpUkFGSWWtC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnQWdH%2FbtsPuFkjHaB%2FizjRH4RmbfcpUkFGSWWtC1%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;347&quot; height=&quot;48&quot; data-origin-width=&quot;347&quot; data-origin-height=&quot;48&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;체인 룰 (Chain Rule)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;49&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n3sR7/btsPsYZ4hJl/hnLuNbGDKXsMKgFJ1fcfp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n3sR7/btsPsYZ4hJl/hnLuNbGDKXsMKgFJ1fcfp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n3sR7/btsPsYZ4hJl/hnLuNbGDKXsMKgFJ1fcfp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn3sR7%2FbtsPsYZ4hJl%2FhnLuNbGDKXsMKgFJ1fcfp0%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;280&quot; height=&quot;49&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;49&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;456&quot; data-origin-height=&quot;139&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvQJji/btsPtmsy7F1/Xt2GK6LrIuOi6r69PimiD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvQJji/btsPtmsy7F1/Xt2GK6LrIuOi6r69PimiD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvQJji/btsPtmsy7F1/Xt2GK6LrIuOi6r69PimiD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvQJji%2FbtsPtmsy7F1%2FXt2GK6LrIuOi6r69PimiD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;456&quot; height=&quot;139&quot; data-origin-width=&quot;456&quot; data-origin-height=&quot;139&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 접선 기울기 계산 예시&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예: &lt;span&gt;f(x) = 3x&lt;/span&gt;&amp;nbsp;일 때, &lt;span&gt;x=2&lt;/span&gt;에서 기울기는 항상 3! (직선이라 일정)&lt;br /&gt;예: &lt;span&gt;g(x) = x^2&lt;/span&gt; 일 때, &lt;span&gt;g&amp;prime;(x) = 2x&lt;/span&gt;&amp;nbsp;이므로, &lt;span&gt;x = 3&lt;/span&gt;에서 기울기는 &lt;span&gt;2&amp;sdot;3 = 6&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&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;4. C 언어로 &amp;ldquo;수치 미분(Numerical Derivative)&amp;rdquo; 구현하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 딥러닝 라이브러리는 자동으로 미분을 계산해 주지만, 개념을 잡기 위해 &lt;b&gt;작은 변화 &lt;span&gt;&amp;Delta;x\Delta x&lt;/span&gt;&lt;/b&gt; 를 이용한 근사 미분 코드를 만들어 봅시다.&lt;/p&gt;
&lt;pre class=&quot;llvm&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;

// 예시 함수 1: f(x) = 3x
double f1(double x) {
    return 3.0 * x;
}

// 예시 함수 2: f(x) = x^2
double f2(double x) {
    return x * x;
}

// 예시 함수 3: h(x) = (3x + 2x^2)^2
double h(double x) {
    double u = 3.0*x + 2.0*x*x;
    return u * u;
}

// 수치 미분: (f(x+h) - f(x)) / h
double numerical_derivative(double (*func)(double), double x) {
    double h = 1e-6;  // 매우 작은 값
    return (func(x + h) - func(x)) / h;
}

int main(void) {
    double x;

    // f1 미분
    x = 2.0;
    printf(&quot;f1(x)=3x,  x=%.2f -&amp;gt; f'(x)&amp;asymp;%.6f  (정답=3.0)\n&quot;,
           x, numerical_derivative(f1, x));

    // f2 미분
    x = 3.0;
    printf(&quot;f2(x)=x^2, x=%.2f -&amp;gt; f'(x)&amp;asymp;%.6f  (정답=2x=6.0)\n&quot;,
           x, numerical_derivative(f2, x));

    // h 미분 (체인룰 예시)
    x = 1.0;
    printf(&quot;h(x)=(3x+2x^2)^2, x=%.2f -&amp;gt; h'(x)&amp;asymp;%.6f  (정답=2u&amp;middot;u' = 2*(3+2)* (3+4*1) = 2*5*7=70)\n&quot;,
           x, numerical_derivative(h, x));

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;어떻게 동작하나요?&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;작은 &lt;span&gt;h&lt;/span&gt;&lt;/b&gt; (&lt;span&gt;10^-6&lt;/span&gt;)를 더한 뒤 함수 값을 구하고, 원래 값과 빼서 &lt;span&gt;&amp;Delta;f&lt;/span&gt;를 얻어요.&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&amp;Delta;f&lt;/span&gt;를 &lt;span&gt;&amp;Delta;x&lt;/span&gt;로 나누면 미분값을 &lt;b&gt;근사&lt;/b&gt;할 수 있습니다.&lt;/li&gt;
&lt;li&gt;실제 수학 식으로 미분한 결과(3.0, 6.0, 70.0)와 아주 가까운 값을 확인할 수 있어요.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마무리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;딥러닝에서 &lt;b&gt;역전파(Backpropagation)&lt;/b&gt; 와 &lt;b&gt;경사하강법(Gradient Descent)&lt;/b&gt; 에는 이 미분 개념이 숨어 있어요.&lt;/li&gt;
&lt;li&gt;오늘 배운 미분 공식을 잘 이해하면, 다음 시간에 &amp;ldquo;오차를 줄이는 방향&amp;rdquo;을 계산할 때 수식이 &lt;b&gt;더 명확&lt;/b&gt;하게 다가올 거예요!&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍/C언어를 이용한 Deep Learning</category>
      <author>지노랩/JinoLab</author>
      <guid isPermaLink="true">https://jinolab.tistory.com/363</guid>
      <comments>https://jinolab.tistory.com/363#entry363comment</comments>
      <pubDate>Sat, 16 Aug 2025 09:21:14 +0900</pubDate>
    </item>
    <item>
      <title>20. &amp;ldquo;근육량 예측 신경망&amp;rdquo;의 순전파(Forward Propagation) 과정을 C 언어로</title>
      <link>https://jinolab.tistory.com/362</link>
      <description>&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;데이터 정규화&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가중치 초기화&lt;/b&gt;(예시로 임의값 사용)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;첫 번째 학습 예제&lt;/b&gt; 순전파&lt;/li&gt;
&lt;li&gt;&lt;b&gt;중간값(z), 활성화(a), 출력(ŷ) 계산&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 문제 정의&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력 &lt;span&gt;(x1,x2)&lt;/span&gt;&amp;nbsp;= (운동시간, 휴식시간)&lt;/li&gt;
&lt;li&gt;출력 &lt;span&gt;y&lt;/span&gt;&amp;nbsp;= 근육량 증가(grams)&lt;/li&gt;
&lt;li&gt;신경망 구조
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력층: 2개 노드&lt;/li&gt;
&lt;li&gt;은닉층: 3개 노드 (활성화: ReLU)&lt;/li&gt;
&lt;li&gt;출력층: 1개 노드 (활성화: Sigmoid)&lt;/li&gt;
&lt;/ul&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;2. 코드 전체&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;math.h&amp;gt;

// --- 활성화 함수들 ---
double relu(double z) {
    return z &amp;gt; 0 ? z : 0;
}
double sigmoid(double z) {
    return 1.0 / (1.0 + exp(-z));
}

// --- 순전파 함수 ---
// X: (m&amp;times;n) 입력 행렬, W1: (n&amp;times;h) 은닉 가중치, W2: (h&amp;times;1) 출력 가중치
// m=샘플 개수(여기선 3), n=피처 수(2), h=은닉 노드 수(3)
void forward(int m, int n, int h,
             double X[m][n],
             double W1[n][h],
             double W2[h][1],
             double Yhat[m])
{
    for (int i = 0; i &amp;lt; m; i++) {
        // 1) 은닉층 z2 = X[i] &amp;middot; W1  (1&amp;times;n)&amp;middot;(n&amp;times;h) &amp;rarr; (1&amp;times;h)
        double z2[ h ];
        for (int j = 0; j &amp;lt; h; j++) {
            z2[j] = 0;
            for (int k = 0; k &amp;lt; n; k++) {
                z2[j] += X[i][k] * W1[k][j];
            }
        }
        // 2) 은닉층 a2 = ReLU(z2)
        double a2[ h ];
        for (int j = 0; j &amp;lt; h; j++) {
            a2[j] = relu(z2[j]);
        }
        // 3) 출력층 z3 = a2 &amp;middot; W2  (1&amp;times;h)&amp;middot;(h&amp;times;1) &amp;rarr; (1&amp;times;1)
        double z3 = 0;
        for (int j = 0; j &amp;lt; h; j++) {
            z3 += a2[j] * W2[j][0];
        }
        // 4) 예측값 yhat = sigmoid(z3)
        Yhat[i] = sigmoid(z3);
    }
}

int main(void) {
    // --- 1) 원본 데이터 (m=3 샘플) ---
    // [운동시간, 휴식시간]
    double rawX[3][2] = {
        {2.0, 8.0},
        {5.0, 5.0},
        {1.0, 8.0}
    };
    // 근육량 증가(grams)
    double rawY[3] = {291.0, 920.0, 190.0};

    // --- 2) 정규화 (0~1) ---
    double maxX0 = 5.0, maxX1 = 8.0, maxY = 920.0;
    double X[3][2], Y[3];
    for (int i = 0; i &amp;lt; 3; i++) {
        X[i][0] = rawX[i][0] / maxX0;
        X[i][1] = rawX[i][1] / maxX1;
        Y[i]    = rawY[i]    / maxY;
    }

    // --- 3) 가중치 초기화(예시값) ---
    // 입력&amp;rarr;은닉 (2&amp;times;3)
    double W1[2][3] = {
        { 0.2, -0.1,  0.4},
        { 0.7,  0.3, -0.5}
    };
    // 은닉&amp;rarr;출력 (3&amp;times;1)
    double W2[3][1] = {
        { 1.0},
        {-1.5},
        { 0.6}
    };

    // --- 4) 순전파 실행 ---
    double Yhat[3];
    forward(3, 2, 3, X, W1, W2, Yhat);

    // --- 5) 결과 출력 (역정규화 포함) ---
    printf(&quot;샘플 | 정답(grams) | 예측(grams)\n&quot;);
    printf(&quot;------+-------------+-------------\n&quot;);
    for (int i = 0; i &amp;lt; 3; i++) {
        printf(&quot;  %d   |   %6.1f    |   %6.1f\n&quot;,
               i+1,
               rawY[i],
               Yhat[i] * maxY  // 역정규화
        );
    }
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 핵심 설명&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;정규화(Normalization)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력&amp;middot;출력 범위를 0~1로 스케일링 &amp;rarr; 학습 안정성&amp;uarr;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;은닉층 계산&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;z2 = XW1&lt;/span&gt;&amp;nbsp;&amp;rarr; &lt;b&gt;ReLU&lt;/b&gt; &amp;rarr; &lt;span&gt;a2&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;출력층 계산&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;z3 = a2W2&lt;/span&gt;&amp;nbsp;&amp;rarr; &lt;b&gt;Sigmoid&lt;/b&gt; &amp;rarr; &lt;span&gt;y^&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예측값 역정규화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;y^ &amp;times; max⁡(Y)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;하나의 함수&lt;/b&gt; forward 에서 &lt;b&gt;반복문으로&lt;/b&gt; 각 샘플을 처리&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 &amp;ldquo;입력 &amp;rarr; 선형결합(z) &amp;rarr; 비선형변환(a) &amp;rarr; 다음 층 입력&amp;rdquo;의 흐름이 바로 &lt;b&gt;순전파&lt;/b&gt;입니다.&lt;br /&gt;다음 시간에는 &lt;b&gt;역전파(Backpropagation)&lt;/b&gt; 를 구현해, 가중치를 학습시키는 방법을 배워봅시다!&lt;/p&gt;</description>
      <category>프로그래밍/C언어를 이용한 Deep Learning</category>
      <author>지노랩/JinoLab</author>
      <guid isPermaLink="true">https://jinolab.tistory.com/362</guid>
      <comments>https://jinolab.tistory.com/362#entry362comment</comments>
      <pubDate>Fri, 15 Aug 2025 09:08:18 +0900</pubDate>
    </item>
    <item>
      <title>19. 활성화 함수(Activation Function)</title>
      <link>https://jinolab.tistory.com/361</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 활성화 함수가 왜 필요할까?&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;선형 함수의 한계&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력값 &lt;span&gt;xx&lt;/span&gt;와 가중치 &lt;span&gt;ww&lt;/span&gt;를 곱하고 더하면 언제나 &amp;ldquo;직선&amp;rdquo; 모양의 결과만 나와요.&lt;/li&gt;
&lt;li&gt;예를 들어,&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span&gt;y=2x,&amp;nbsp; y=x+3&amp;nbsp; &amp;nbsp;&lt;/span&gt;이런 식만 표현할 수 있죠.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비선형 패턴 학습&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현실 세계의 데이터(이미지, 음성, 글자 등)는 아주 복잡해요.&lt;/li&gt;
&lt;li&gt;&amp;ldquo;직선&amp;rdquo;만으로는 사람 얼굴의 곡선, 음성의 억양 같은 복잡한 패턴을 배우기 어려워요.&lt;/li&gt;
&lt;li&gt;그래서 **활성화 함수(Activation Function)**로 입력을 곡선 형태로 변환해야, 여러 층(layer)을 쌓았을 때 다양한 곡선 모양을 만들 수 있어요.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;역전파(Backpropagation)를 위한 미분 가능성&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;학습 과정에서 &amp;ldquo;실제값과 예측값의 차이&amp;rdquo;를 줄이려고 가중치 &lt;span&gt;ww&lt;/span&gt;를 업데이트할 때, 미분(derivative)이 필요해요.&lt;/li&gt;
&lt;li&gt;활성화 함수는 &lt;b&gt;미분하기 쉬운(non‑linear but differentiable)&lt;/b&gt; 함수여야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 대표적인 활성화 함수 3가지&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 시그모이드(Sigmoid)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;공식&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;154&quot; data-origin-height=&quot;55&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdNzwk/btsPtEGr3d0/cLdJrTtfykkf1wKJetKIe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdNzwk/btsPtEGr3d0/cLdJrTtfykkf1wKJetKIe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdNzwk/btsPtEGr3d0/cLdJrTtfykkf1wKJetKIe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdNzwk%2FbtsPtEGr3d0%2FcLdJrTtfykkf1wKJetKIe0%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;154&quot; height=&quot;55&quot; data-origin-width=&quot;154&quot; data-origin-height=&quot;55&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;특징&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;출력값이 항상 &lt;span&gt;0&lt;/span&gt;과 &lt;span&gt;1&lt;/span&gt; 사이&lt;/li&gt;
&lt;li&gt;S자 곡선&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장단점&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장점: 결과를 &amp;ldquo;확률&amp;rdquo;처럼 해석할 수 있어요.&lt;/li&gt;
&lt;li&gt;단점: 입력값이 크면 기울기가 0에 가까워져(평평해져) 학습 속도가 느려지는 &lt;b&gt;소실 기울기&lt;/b&gt; 문제가 생겨요.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 하이퍼볼릭 탄젠트(Tanh)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;공식&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;174&quot; data-origin-height=&quot;55&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KcUmo/btsPur0DdqY/a72wKLjPnC9LW1GCW7x6j1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KcUmo/btsPur0DdqY/a72wKLjPnC9LW1GCW7x6j1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KcUmo/btsPur0DdqY/a72wKLjPnC9LW1GCW7x6j1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKcUmo%2FbtsPur0DdqY%2Fa72wKLjPnC9LW1GCW7x6j1%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;174&quot; height=&quot;55&quot; data-origin-width=&quot;174&quot; data-origin-height=&quot;55&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;특징&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;출력값이 &lt;span&gt;&amp;minus;1&lt;/span&gt;과 &lt;span&gt;1&lt;/span&gt; 사이&lt;/li&gt;
&lt;li&gt;시그모이드보다 중앙(0 근처)에서 더 가파른 기울기&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장단점&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장점: &lt;span&gt;-1&lt;/span&gt;~1 범위라 평균이 0에 가까워 학습이 조금 더 안정적일 수 있어요.&lt;/li&gt;
&lt;li&gt;단점: 여전히 큰 입력에서 평평해지는 &lt;b&gt;기울기 소실&lt;/b&gt;이 남아요.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.3 ReLU(Rectified Linear Unit)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;공식&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;193&quot; data-origin-height=&quot;36&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/osTzn/btsPsZxPpYf/BeTaQ4jTyeKWeqlkJUBRB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/osTzn/btsPsZxPpYf/BeTaQ4jTyeKWeqlkJUBRB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/osTzn/btsPsZxPpYf/BeTaQ4jTyeKWeqlkJUBRB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FosTzn%2FbtsPsZxPpYf%2FBeTaQ4jTyeKWeqlkJUBRB1%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;193&quot; height=&quot;36&quot; data-origin-width=&quot;193&quot; data-origin-height=&quot;36&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;특징&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;음수 구간은 0, 양수 구간은 직선 상승&lt;/li&gt;
&lt;li&gt;계산이 매우 간단해 대규모 신경망에서 효율적&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장단점&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장점: 기울기 소실 문제 거의 없고 학습이 빠름&lt;/li&gt;
&lt;li&gt;단점: 음수 구간에서 기울기가 0이 되어 &lt;b&gt;죽은 뉴런&lt;/b&gt;이 생길 수 있어요 (한번 0이 되면 계속 0)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 수식으로 보는 도함수(미분)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;199&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MnN7y/btsPs5dvIKM/kcntXvWO7XtFXhBRfSqJP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MnN7y/btsPs5dvIKM/kcntXvWO7XtFXhBRfSqJP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MnN7y/btsPs5dvIKM/kcntXvWO7XtFXhBRfSqJP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMnN7y%2FbtsPs5dvIKM%2FkcntXvWO7XtFXhBRfSqJP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;486&quot; height=&quot;199&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;199&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;학습(역전파) 때, 이 도함수를 사용해 &amp;ldquo;오차가 어떻게 변하는지&amp;rdquo; 를 계산합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 언제 어떤 함수를 쓸까?&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 Layer)&lt;/b&gt;: 대부분 &lt;b&gt;ReLU&lt;/b&gt;를 씁니다. 빠르고 계산도 간단해요.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;출력층(Output Layer)&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이진 분류 &amp;rarr; Sigmoid&lt;/li&gt;
&lt;li&gt;다중 분류 &amp;rarr; Softmax (여기선 다루지 않음)&lt;/li&gt;
&lt;li&gt;회귀(regression) &amp;rarr; 항등 함수(Identity): &lt;span&gt;f(z)=z&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마무리&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;선형&lt;/b&gt;만으로는 복잡한 패턴을 못 배우니, &lt;b&gt;비선형&lt;/b&gt; 활성화 함수가 필요해요.&lt;/li&gt;
&lt;li&gt;Sigmoid, Tanh, ReLU의 &lt;b&gt;모양&lt;/b&gt;과 &lt;b&gt;장단점&lt;/b&gt;을 이해하세요.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;미분 가능&lt;/b&gt;해야 역전파 학습이 가능해요.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 활성화 함수의 개념을 잡았으니, 다음 시간에는 &lt;b&gt;직접 코드&lt;/b&gt;로 구현해 보고, 신경망이 학습할 수 있도록 만들어 봅시다!&lt;/p&gt;</description>
      <category>프로그래밍/C언어를 이용한 Deep Learning</category>
      <author>지노랩/JinoLab</author>
      <guid isPermaLink="true">https://jinolab.tistory.com/361</guid>
      <comments>https://jinolab.tistory.com/361#entry361comment</comments>
      <pubDate>Thu, 14 Aug 2025 09:02:47 +0900</pubDate>
    </item>
    <item>
      <title>18. 은닉층&amp;middot;출력층 가중치 행렬</title>
      <link>https://jinolab.tistory.com/360</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;은닉층&amp;middot;출력층 가중치 행렬을 0~1 사이의 난수로 초기화&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;random_init(rows, cols, W) 함수를 통해 임의의 실수를 생성해 W[i][j]에 저장&lt;/li&gt;
&lt;li&gt;RAND_MAX를 이용해 rand()/(double)RAND_MAX 형태로 0~1 구간 난수 생성&lt;/li&gt;
&lt;li&gt;srand(time(NULL))으로 시드 초기화&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;time.h&amp;gt;

/**
 * @brief rows&amp;times;cols 크기의 행렬 W 를 0~1 난수로 채웁니다.
 * @param rows  행(첫 차원) 크기
 * @param cols  열(둘째 차원) 크기
 * @param W     결과를 저장할 2D 배열 (row-major)
 */
void random_init(int rows, int cols, double W[rows][cols]) {
    // 난수 시드: 실행할 때마다 다른 값 생성
    srand((unsigned)time(NULL));
    for (int i = 0; i &amp;lt; rows; i++) {
        for (int j = 0; j &amp;lt; cols; j++) {
            // 0 &amp;lt;= rand()/RAND_MAX &amp;lt; 1
            W[i][j] = rand() / (double)RAND_MAX;
        }
    }
}

int main(void) {
    // --- 네트워크 구조 예시 ---
    // 입력층 &amp;rarr; 은닉층 (2&amp;rarr;3), 은닉층 &amp;rarr; 출력층 (3&amp;rarr;1)
    const int N_INPUT  = 2;  // 입력 노드 수
    const int N_HIDDEN = 3;  // 은닉 노드 수
    const int N_OUTPUT = 1;  // 출력 노드 수

    // 가중치 행렬 버퍼 선언
    double W1[N_INPUT][N_HIDDEN];  // (2&amp;times;3) 행렬: 입력&amp;rarr;은닉
    double W2[N_HIDDEN][N_OUTPUT]; // (3&amp;times;1) 행렬: 은닉&amp;rarr;출력

    // --- 난수 초기화 호출 ---
    random_init(N_INPUT,  N_HIDDEN, W1);
    random_init(N_HIDDEN, N_OUTPUT, W2);

    // --- 초기화된 가중치 출력 ---
    printf(&quot;W1 (입력&amp;rarr;은닉  %d&amp;times;%d):\n&quot;, N_INPUT, N_HIDDEN);
    for (int i = 0; i &amp;lt; N_INPUT; i++) {
        for (int j = 0; j &amp;lt; N_HIDDEN; j++) {
            printf(&quot;%6.4f &quot;, W1[i][j]);
        }
        printf(&quot;\n&quot;);
    }
    printf(&quot;\n&quot;);

    printf(&quot;W2 (은닉&amp;rarr;출력  %d&amp;times;%d):\n&quot;, N_HIDDEN, N_OUTPUT);
    for (int i = 0; i &amp;lt; N_HIDDEN; i++) {
        for (int j = 0; j &amp;lt; N_OUTPUT; j++) {
            printf(&quot;%6.4f &quot;, W2[i][j]);
        }
        printf(&quot;\n&quot;);
    }
    printf(&quot;\n&quot;);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설명&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;random_init 함수&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;srand(time(NULL))로 한 번만 시드를 설정&lt;/li&gt;
&lt;li&gt;rand() / (double)RAND_MAX로 &lt;span&gt;[0,1)[0,1)&lt;/span&gt; 구간의 실수 난수 생성&lt;/li&gt;
&lt;li&gt;이중 for문으로 행&amp;middot;열 순회하며 W[i][j]에 대입&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;main 함수&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예시로 &lt;b&gt;입력 2 &amp;rarr; 은닉 3 &amp;rarr; 출력 1&lt;/b&gt; 구조 사용&lt;/li&gt;
&lt;li&gt;W1[2][3], W2[3][1] 배열 선언 후 random_init 호출&lt;/li&gt;
&lt;li&gt;초기화된 행렬을 [row][col] 형태로 보기 좋게 출력&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이 가중치 행렬을 이용해 &lt;b&gt;순전파&lt;/b&gt;(Forward Propagation)와 &lt;b&gt;역전파&lt;/b&gt;(Backpropagation)를 구현하면, 완전한 학습 루프를 구성할 수 있습니다.&lt;/p&gt;</description>
      <category>프로그래밍/C언어를 이용한 Deep Learning</category>
      <author>지노랩/JinoLab</author>
      <guid isPermaLink="true">https://jinolab.tistory.com/360</guid>
      <comments>https://jinolab.tistory.com/360#entry360comment</comments>
      <pubDate>Wed, 13 Aug 2025 09:56:38 +0900</pubDate>
    </item>
    <item>
      <title>17. 데이터 정규화(Normalization) 과정</title>
      <link>https://jinolab.tistory.com/359</link>
      <description>&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 벡터의 최댓값을 찾아&lt;/li&gt;
&lt;li&gt;해당 최댓값으로 모든 원소를 나누어&lt;/li&gt;
&lt;li&gt;[0,1] 구간으로 스케일링 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;

/**
 * @brief 1차원 배열에서 최댓값을 찾습니다.
 * @param arr   입력 배열
 * @param n     배열 길이
 * @return      최댓값
 */
double max_in_array(const double *arr, int n) {
    double m = arr[0];
    for (int i = 1; i &amp;lt; n; i++) {
        if (arr[i] &amp;gt; m) {
            m = arr[i];
        }
    }
    return m;
}

/**
 * @brief 배열을 [0,1] 구간으로 정규화합니다.
 *        각 원소를 최댓값으로 나눈 결과를 out[]에 저장.
 * @param in    입력 배열
 * @param out   정규화 결과 저장 배열 (동일 길이)
 * @param n     배열 길이
 */
void normalize_data(const double *in, double *out, int n) {
    double m = max_in_array(in, n);
    for (int i = 0; i &amp;lt; n; i++) {
        out[i] = in[i] / m;
    }
}

int main(void) {
    // ── 1) 원본 데이터 ──
    // 3개의 학습 예시(each row): [운동시간, 휴식시간] &amp;rarr; 근육증가량
    const int M = 3;        // 샘플 개수
    double x1[M]   = {2.0, 5.0, 1.0};     // 운동시간(hours)
    double x2[M]   = {8.0, 5.0, 8.0};     // 휴식시간(hours)
    double y[M]    = {291.0, 920.0, 190.0};// 근육증가량(grams)

    // ── 2) 정규화 결과 저장 버퍼 ──
    double x1_n[M], x2_n[M], y_n[M];

    // ── 3) 정규화 수행 ──
    normalize_data(x1, x1_n, M);
    normalize_data(x2, x2_n, M);
    normalize_data(y,  y_n,  M);

    // ── 4) 결과 출력 ──
    printf(&quot;원본    | 정규화\n&quot;);
    printf(&quot;--------+----------------------------\n&quot;);
    printf(&quot;Idx | x1   x2   y    | x1_n    x2_n    y_n\n&quot;);
    printf(&quot;----+--------------------------------------\n&quot;);
    for (int i = 0; i &amp;lt; M; i++) {
        printf(&quot;%3d | %4.1f %4.1f %4.1f | %7.4f %7.4f %7.4f\n&quot;,
               i+1, x1[i], x2[i], y[i],
               x1_n[i], x2_n[i], y_n[i]);
    }

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실행 예시&lt;/h3&gt;
&lt;pre class=&quot;glsl&quot;&gt;&lt;code&gt;gcc -o normalize normalize.c
./normalize
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;원본    | 정규화
--------+----------------------------
Idx | x1   x2   y    | x1_n    x2_n    y_n
----+--------------------------------------
  1 |  2.0  8.0 291.0 | 0.4000  1.0000  0.3169
  2 |  5.0  5.0 920.0 | 1.0000  0.6250  1.0000
  3 |  1.0  8.0 190.0 | 0.2000  1.0000  0.2065
&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;max_in_array&lt;/b&gt;: 배열 내 최댓값을 찾아 반환&lt;/li&gt;
&lt;li&gt;&lt;b&gt;normalize_data&lt;/b&gt;: 각 원소를 최댓값으로 나누어 [0,1] 구간으로 스케일링&lt;/li&gt;
&lt;li&gt;&lt;b&gt;main&lt;/b&gt;:
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;원본 x1, x2, y 정의&lt;/li&gt;
&lt;li&gt;정규화 결과를 받을 x1_n, x2_n, y_n 선언&lt;/li&gt;
&lt;li&gt;normalize_data 호출&lt;/li&gt;
&lt;li&gt;원본과 정규화된 값을 나란히 출력&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이 정규화된 데이터를 신경망에 입력으로 사용하면, &lt;b&gt;입력값과 출력값의 스케일 차이&lt;/b&gt;로 인한 학습 불안정 문제를 완화할 수 있습니다.&lt;/p&gt;</description>
      <category>프로그래밍/C언어를 이용한 Deep Learning</category>
      <author>지노랩/JinoLab</author>
      <guid isPermaLink="true">https://jinolab.tistory.com/359</guid>
      <comments>https://jinolab.tistory.com/359#entry359comment</comments>
      <pubDate>Tue, 12 Aug 2025 09:53:22 +0900</pubDate>
    </item>
    <item>
      <title>16. 헬스장 데이터(운동 시간&amp;middot;휴식 시간 &amp;rarr; 근육량 증가) Forward Propagation</title>
      <link>https://jinolab.tistory.com/358</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 &amp;ldquo;헬스장 데이터&amp;rdquo;(운동 시간&amp;middot;휴식 시간 &amp;rarr; 근육량 증가) 예제를 C 언어로 구현한 &lt;b&gt;앞으로 전파(Forward Propagation)&lt;/b&gt; 코드입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;데이터 전처리&lt;/b&gt;: 원본 데이터를 최대값으로 나눠 [0,1] 구간으로 정규화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;네트워크 구조&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력층: 2개 특성(운동 시간, 휴식 시간)&lt;/li&gt;
&lt;li&gt;은닉층: 3개 유닛 (ReLU 활성화)&lt;/li&gt;
&lt;li&gt;출력층: 1개 유닛 (항등 활성화)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;

// --- 활성화 함수들 ---
double relu(double x) {
    return x &amp;gt; 0 ? x : 0;
}

// --- 행렬&amp;middot;벡터 곱: z = X (m&amp;times;n) &amp;middot; W (n&amp;times;p) --&amp;gt;
//    result is Z (m&amp;times;p)
void matmul(const double *X, int m, int n,
            const double *W, int p,
            double *Z) {
    // X row-major: X[i*n + k]
    // W row-major: W[k*p + j]
    for (int i = 0; i &amp;lt; m; i++) {
        for (int j = 0; j &amp;lt; p; j++) {
            double sum = 0.0;
            for (int k = 0; k &amp;lt; n; k++) {
                sum += X[i*n + k] * W[k*p + j];
            }
            Z[i*p + j] = sum;
        }
    }
}

int main(void) {
    // --- 1) 원본 데이터 (3번의 세션) ---
    // 운동 시간(hours), 휴식 시간(hours), 근육 증가량(grams)
    double raw_X[3][2] = {
        {1.0, 2.0},   // 예: 1시간 운동, 2시간 휴식 &amp;rarr; 300g 증가
        {2.0, 1.0},   //      2시간 운동, 1시간 휴식 &amp;rarr; 500g 증가
        {3.0, 1.5}    //      3시간 운동, 1.5시간 휴식 &amp;rarr; 650g 증가
    };
    double raw_y[3] = {300.0, 500.0, 650.0};

    // --- 2) 정규화 --- (각 컬럼의 최대값으로 나누기)
    double max_x0 = 3.0, max_x1 = 2.0, max_y = 650.0;
    double X[3][2], y[3];
    for (int i = 0; i &amp;lt; 3; i++) {
        X[i][0] = raw_X[i][0] / max_x0;
        X[i][1] = raw_X[i][1] / max_x1;
        y[i]    = raw_y[i] / max_y;  // (0~1로 스케일링)
    }

    // --- 3) 네트워크 가중치 초기값 ---
    // W1: 입력(2) &amp;rarr; 은닉(3)
    double W1[2][3] = {
        { 0.5, -0.3,  0.8},
        { 0.2,  0.4, -0.5}
    };
    // W2: 은닉(3) &amp;rarr; 출력(1)
    double W2[3][1] = {
        { 1.0},
        {-1.2},
        { 0.5}
    };

    // --- 4) Forward Propagation ---
    // 4-1. Z2 = X &amp;middot; W1    (3&amp;times;2)&amp;middot;(2&amp;times;3) = (3&amp;times;3)
    double Z2[3][3];
    matmul(&amp;amp;X[0][0], 3, 2, &amp;amp;W1[0][0], 3, &amp;amp;Z2[0][0]);

    // 4-2. A2 = ReLU(Z2)  (3&amp;times;3)
    double A2[3][3];
    for (int i = 0; i &amp;lt; 3; i++)
        for (int j = 0; j &amp;lt; 3; j++)
            A2[i][j] = relu(Z2[i][j]);

    // 4-3. Z3 = A2 &amp;middot; W2   (3&amp;times;3)&amp;middot;(3&amp;times;1) = (3&amp;times;1)
    double Z3[3][1];
    matmul(&amp;amp;A2[0][0], 3, 3, &amp;amp;W2[0][0], 1, &amp;amp;Z3[0][0]);

    // 4-4. y_hat = Z3     (항등 활성화)
    double y_hat[3];
    for (int i = 0; i &amp;lt; 3; i++) {
        y_hat[i] = Z3[i][0];
    }

    // --- 5) 결과 출력 (역정규화 포함) ---
    printf(&quot;예측근육증가량(grams) vs 실제(grams):\n&quot;);
    for (int i = 0; i &amp;lt; 3; i++) {
        double pred_g = y_hat[i] * max_y;  // 역정규화
        printf(&quot;  입력(%.1fh, %.1fh) &amp;rarr; 예측: %6.1fg, 실제: %6.1fg\n&quot;,
               raw_X[i][0], raw_X[i][1],
               pred_g, raw_y[i]);
    }

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 설명&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;데이터 정규화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력 X[i][j] = raw_X[i][j] / max(raw_X[:,j])&lt;/li&gt;
&lt;li&gt;출력 y[i] = raw_y[i] / max(raw_y)&lt;br /&gt;&amp;rarr; 모든 값이 [0,1] 구간에 있도록 스케일링&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;행렬 곱 함수(matmul)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반적인 &lt;span&gt;Z=XW&lt;/span&gt;&amp;nbsp;연산을 수행하기 위한 3중 for‑loop 구현&lt;/li&gt;
&lt;li&gt;X는 &lt;span&gt;m&amp;times;n&lt;/span&gt;, W는 &lt;span&gt;n&amp;times;p&lt;/span&gt;, 결과 Z는 &lt;span&gt;m&amp;times;p&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;순전파(Forward Propagation)&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Z₂ = X&amp;middot;W₁&lt;/b&gt; &amp;rarr; 은닉층 선형결합&lt;/li&gt;
&lt;li&gt;&lt;b&gt;A₂ = ReLU(Z₂)&lt;/b&gt; &amp;rarr; 은닉층 비선형 활성화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Z₃ = A₂&amp;middot;W₂&lt;/b&gt; &amp;rarr; 출력층 선형결합&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ŷ = Z₃&lt;/b&gt; &amp;rarr; 항등(Identity) 활성화&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;역정규화 후 출력 비교&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델이 예측한 &lt;span&gt;y^&lt;/span&gt;를 다시 원단위(grams)로 변환하여(ŷ * max_y) 실제값과 비교&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예제는 &lt;b&gt;모델 학습 없이&lt;/b&gt;, 임의의 가중치로 &amp;ldquo;순전파&amp;rdquo;만 시연합니다. 이후 &lt;b&gt;경사하강법(Gradient Descent)&lt;/b&gt; 및 &lt;b&gt;역전파(Backpropagation)&lt;/b&gt; 과정을 통해 W1, W2를 학습시키면, 주어진 운동&amp;middot;휴식 시간에 따른 근육량 예측 성능을 개선할 수 있습니다.&lt;/p&gt;</description>
      <category>프로그래밍/C언어를 이용한 Deep Learning</category>
      <author>지노랩/JinoLab</author>
      <guid isPermaLink="true">https://jinolab.tistory.com/358</guid>
      <comments>https://jinolab.tistory.com/358#entry358comment</comments>
      <pubDate>Mon, 11 Aug 2025 09:51:19 +0900</pubDate>
    </item>
    <item>
      <title>15. 생물학적 뉴런의 기능과 인공 뉴런 비교</title>
      <link>https://jinolab.tistory.com/357</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYrNU1/btsPsRF2C03/kS2SJVIwlEk9AcbtAt4ud0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYrNU1/btsPsRF2C03/kS2SJVIwlEk9AcbtAt4ud0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYrNU1/btsPsRF2C03/kS2SJVIwlEk9AcbtAt4ud0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYrNU1%2FbtsPsRF2C03%2FkS2SJVIwlEk9AcbtAt4ud0%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;338&quot; height=&quot;338&quot; data-origin-width=&quot;1024&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;1. 입력과 가중치&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;생물학적 뉴런&lt;/b&gt;: 수많은 다른 뉴런의 신호가 가지돌기(수상돌기)를 통해 들어오며, 각 연결부위(시냅스)의 &amp;ldquo;강도(가중)&amp;rdquo;에 따라 영향력이 달라짐&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인공 뉴런&lt;/b&gt;: 입력 벡터 &lt;span&gt;x=[x1,x2,&amp;hellip;,xn]&lt;/span&gt;와 대응하는 가중치 벡터 &lt;span&gt;w=[w1,w2,&amp;hellip;,wn]&lt;/span&gt;를 가짐&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비유&lt;/b&gt;: &lt;span&gt;wi&lt;/span&gt;는 생물학적 시냅스 강도에 해당&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 합산 (Summation)&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;생물학적 뉴런&lt;/b&gt;: 세포체에서 모든 시냅스 후 전위가 합쳐져 막 전위가 결정됨&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인공 뉴런&lt;/b&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;119&quot; data-origin-height=&quot;64&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/di1N1o/btsPtZpxcPi/E5qSD6KK21EPgCnlSRL5f0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/di1N1o/btsPtZpxcPi/E5qSD6KK21EPgCnlSRL5f0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/di1N1o/btsPtZpxcPi/E5qSD6KK21EPgCnlSRL5f0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdi1N1o%2FbtsPtZpxcPi%2FE5qSD6KK21EPgCnlSRL5f0%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;119&quot; height=&quot;64&quot; data-origin-width=&quot;119&quot; data-origin-height=&quot;64&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로, 입력값과 가중치를 곱한 뒤 모두 더해 단일 스칼라 값 &lt;span&gt;zz&lt;/span&gt;를 얻음&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 활성화 함수 (Activation)&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;생물학적 뉴런&lt;/b&gt;: 막 전위가 임계치(threshold)를 넘으면 일종의 &amp;lsquo;발화(spike)&amp;rsquo; 신호를 축삭(axon)을 통해 멀리 전달&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인공 뉴런&lt;/b&gt;: &lt;span&gt;z&lt;/span&gt;에 비선형 함수를 적용하여 출력값을 계산
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;계단 함수&lt;/b&gt;(Step): &lt;span&gt;ϕ(z)=1&lt;/span&gt; (발화) 또는 &lt;span&gt;0&lt;/span&gt; (비발화)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;시그모이드&lt;/b&gt;(Sigmoid): &lt;span&gt;ϕ(z)=11+e&amp;minus;z\phi(z)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ReLU&lt;/b&gt;: &lt;span&gt;ϕ(z)=max⁡(0,&amp;thinsp;z)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 출력&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;생물학적 뉴런&lt;/b&gt;: 축삭을 통해 전기 신호(액션 포텐셜)가 다음 뉴런으로 전달&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인공 뉴런&lt;/b&gt;:&lt;span&gt;y=ϕ(z) &lt;/span&gt;이 값을 다음 층의 뉴런 입력으로 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인공 뉴런 요약 다이어그램&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;입력 x₁ ─┐
         ▼
       [&amp;times; w₁]  
입력 x₂ ─┐      &amp;Sigma;(z) ──▶ &amp;phi;(z) ── 출력 y
       [&amp;times; w₂]  
   &amp;hellip;    &amp;hellip;
입력 xₙ ─┐
       [&amp;times; wₙ]
&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;각 입력 &lt;span&gt;xi&lt;/span&gt;에 가중치 &lt;span&gt;wi&lt;/span&gt;를 곱한다.&lt;/li&gt;
&lt;li&gt;곱셈 결과를 모두 더해 &lt;span&gt;z&lt;/span&gt;를 계산한다.&lt;/li&gt;
&lt;li&gt;비선형 활성화 함수 &lt;span&gt;ϕ(z)&lt;/span&gt;를 적용한다.&lt;/li&gt;
&lt;li&gt;최종 출력 &lt;span&gt;y=ϕ(z)&lt;/span&gt;를 얻는다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 생물학적 뉴런의 구조&amp;middot;기능을 단순화하여 만든 인공 뉴런을 여러 개 연결하면 &amp;ldquo;신경망(Neural Network)&amp;rdquo;이 되며, 이를 통해 이미지 인식&amp;middot;자연어 처리&amp;middot;추천 시스템 등 다양한 문제를 풀 수 있습니다.&lt;/p&gt;</description>
      <category>프로그래밍/C언어를 이용한 Deep Learning</category>
      <author>지노랩/JinoLab</author>
      <guid isPermaLink="true">https://jinolab.tistory.com/357</guid>
      <comments>https://jinolab.tistory.com/357#entry357comment</comments>
      <pubDate>Sun, 10 Aug 2025 09:47:40 +0900</pubDate>
    </item>
    <item>
      <title>14. 경사하강법(Gradient Descent)</title>
      <link>https://jinolab.tistory.com/356</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 경사하강법이란?&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;목표&lt;/b&gt;: 손실함수(loss function) &lt;span&gt;E(w)&lt;/span&gt; 를 최소화하는 가중치 &lt;span&gt;w*&lt;/span&gt; 를 찾는 방법&lt;/li&gt;
&lt;li&gt;&lt;b&gt;손실함수 예시&lt;/b&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;292&quot; data-origin-height=&quot;31&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxvEEG/btsProkvXEM/TmejpiFK0txTTSVvI2MKek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxvEEG/btsProkvXEM/TmejpiFK0txTTSVvI2MKek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxvEEG/btsProkvXEM/TmejpiFK0txTTSVvI2MKek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxvEEG%2FbtsProkvXEM%2FTmejpiFK0txTTSVvI2MKek%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;292&quot; height=&quot;31&quot; data-origin-width=&quot;292&quot; data-origin-height=&quot;31&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;경사(gradient)&lt;/b&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;166&quot; data-origin-height=&quot;57&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVE8ws/btsPstSVEu4/blr8CBcBhPWGhr5Pg7PKS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVE8ws/btsPstSVEu4/blr8CBcBhPWGhr5Pg7PKS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVE8ws/btsPstSVEu4/blr8CBcBhPWGhr5Pg7PKS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVE8ws%2FbtsPstSVEu4%2Fblr8CBcBhPWGhr5Pg7PKS1%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;166&quot; height=&quot;57&quot; data-origin-width=&quot;166&quot; data-origin-height=&quot;57&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;업데이트 룰&lt;/b&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;152&quot; data-origin-height=&quot;52&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRLg2O/btsPt1AROrP/NczyMoXSkrc3vpCzUzaMzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRLg2O/btsPt1AROrP/NczyMoXSkrc3vpCzUzaMzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRLg2O/btsPt1AROrP/NczyMoXSkrc3vpCzUzaMzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRLg2O%2FbtsPt1AROrP%2FNczyMoXSkrc3vpCzUzaMzk%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;152&quot; height=&quot;52&quot; data-origin-width=&quot;152&quot; data-origin-height=&quot;52&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&amp;alpha;&lt;/span&gt;: 학습률(learning rate)&lt;/li&gt;
&lt;li&gt;매 단계마다 &lt;span&gt;dE/dw&lt;/span&gt;의 &amp;ldquo;방향&amp;rdquo;과 &amp;ldquo;크기&amp;rdquo;를 참고해 &lt;span&gt;w&lt;/span&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;2. 시각적 이해&lt;/h2&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;   E(w)
    ▲
    │         ●  
    │        / \
    │       /   \
    │      /     \
    │     ●       ●   &amp;larr; 목표점(기울기=0, 최소 손실)
    │    /         \
    │   /           \
    │  ●             ●
    └──────────────────▶ w
            ▲
       현재 위치
&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;현재 위치&lt;/b&gt;(노란 점)에서 &lt;span&gt;E(w)E(w)&lt;/span&gt; 기울기를 계산&lt;/li&gt;
&lt;li&gt;기울기가 양수면 &lt;span&gt;ww&lt;/span&gt;를 &lt;b&gt;내려(&amp;ndash;)&lt;/b&gt;, 음수면 &lt;b&gt;올려(+)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;여러 번 반복하면(&amp;rarr; 화살표) 최저점(기울기 = 0)에 수렴&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. C 언어 구현: 단일 매개변수 경사하강법&lt;/h2&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;

// 손실함수: E(w) = (x*w - y)^2
double loss(double x, double w, double y) {
    double diff = x * w - y;
    return diff * diff;
}

// 손실함수의 기울기 dE/dw = 2 * (x*w - y) * x
double gradient(double x, double w, double y) {
    return 2.0 * (x * w - y) * x;
}

int main(void) {
    double x       = 0.5;    // 입력값
    double y_true  = 0.8;    // 목표(정답)
    double w       = 0.0;    // 초기 가중치
    double alpha   = 0.1;    // 학습률
    int    epochs  = 20;     // 반복 횟수

    printf(&quot;Epoch |     w     |   loss   \n&quot;);
    printf(&quot;--------------------------------\n&quot;);
    for (int i = 1; i &amp;lt;= epochs; i++) {
        double grad = gradient(x, w, y_true);
        w -= alpha * grad;                   // 경사하강 업데이트
        double L = loss(x, w, y_true);
        printf(&quot;%5d | %8.4f | %8.4f\n&quot;, i, w, L);
    }
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;빌드 &amp;amp; 실행&lt;/h3&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;gcc -o gd_single gd_single.c
./gd_single
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Epoch |     w     |   loss   
--------------------------------
    1 |   0.0800 |   0.2704
    2 |   0.1440 |   0.1564
    3 |   0.1958 |   0.0904
    4 |   0.2373 |   0.0522
    5 |   0.2701 |   0.0302
    ...
   20 |   0.7960 |   0.0003
&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;설명&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;gradient() 함수가 기울기(손실 증가 방향)를 계산&lt;/li&gt;
&lt;li&gt;w -= alpha * grad; 로 손실이 줄어드는 반대 방향(하강)으로 이동&lt;/li&gt;
&lt;li&gt;반복할수록 손실(loss)가 0에 가까워지고, 가중치 w가 y_true / x에 수렴&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 팁 &amp;amp; 확장&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;학습률(&amp;alpha;) 조정&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;너무 크면 발산, 너무 작으면 느린 수렴&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;다차원 경사하강법&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력 벡터 &lt;span&gt;x\mathbf{x}&lt;/span&gt;, 가중치 벡터 &lt;span&gt;w\mathbf{w}&lt;/span&gt;로 일반화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;미니배치&amp;middot;배치 학습&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 샘플 평균 기울기를 사용해 안정적 학습&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;모멘텀, Adam&lt;/b&gt; 등 고급 옵티마이저 적용 가능&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 경사하강법은 &amp;ldquo;현재 손실 그래프에서 기울기를 구해, 그 반대 방향으로 조금씩 이동하는&amp;rdquo; 매우 직관적인 학습 알고리즘입니다. 다음 강의에서는 &lt;b&gt;다층 퍼셉트론&lt;/b&gt;에 적용된 &lt;b&gt;역전파(backpropagation)&lt;/b&gt; 를 살펴보겠습니다!&lt;/p&gt;</description>
      <category>프로그래밍/C언어를 이용한 Deep Learning</category>
      <author>지노랩/JinoLab</author>
      <guid isPermaLink="true">https://jinolab.tistory.com/356</guid>
      <comments>https://jinolab.tistory.com/356#entry356comment</comments>
      <pubDate>Sat, 9 Aug 2025 09:41:39 +0900</pubDate>
    </item>
    <item>
      <title>13. 브루트 포스 학습(Brute‑Force Learning)</title>
      <link>https://jinolab.tistory.com/355</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 &amp;ldquo;브루트 포스 학습(Brute‑Force Learning)&amp;rdquo;이라 부르는, &lt;b&gt;가중치를 한 스텝씩 올려보거나 내려보면서&lt;/b&gt; 오차가 줄어드는 방향으로 학습하는 &lt;b&gt;단일 퍼셉트론&lt;/b&gt; C 예제입니다. 이해를 돕기 위해 &lt;b&gt;상세 주석&lt;/b&gt;과 함께, 반복(iterations)마다 가중치&amp;middot;예측값&amp;middot;오차가 어떻게 변하는지 출력합니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;math.h&amp;gt;

/**
 * @brief 제곱 오차 계산: (y_pred - y_true)^2
 */
double squared_error(double y_pred, double y_true) {
    double diff = y_pred - y_true;
    return diff * diff;
}

/**
 * @brief 브루트 포스 학습 시뮬레이션
 * @param x            입력값
 * @param w            초기 가중치 (참조로 전달하여 업데이트)
 * @param y_true       실제값(정답)
 * @param step         가중치 증감 스텝(학습률 &amp;alpha; 역할)
 * @param iterations   반복 횟수(에포크 수)
 */
void brute_force_learn(double x, double *w, double y_true,
                       double step, int iterations) {
    printf(&quot;=== Brute‑Force Learning Start ===\n&quot;);
    printf(&quot;Init weight = %.6f, input x = %.6f, target y = %.6f\n\n&quot;,
           *w, x, y_true);

    for (int i = 1; i &amp;lt;= iterations; i++) {
        // 1) 현재 가중치로 예측값과 오차 계산
        double y_pred      = x * (*w);
        double err_current = squared_error(y_pred, y_true);

        // 2) 가중치를 올렸을 때의 오차
        double w_up        = *w + step;
        double y_pred_up   = x * w_up;
        double err_up      = squared_error(y_pred_up, y_true);

        // 3) 가중치를 내렸을 때의 오차
        double w_down      = *w - step;
        double y_pred_down = x * w_down;
        double err_down    = squared_error(y_pred_down, y_true);

        // 4) 오차가 더 작아지는 방향으로 가중치 업데이트
        if (err_up &amp;lt; err_down &amp;amp;&amp;amp; err_up &amp;lt; err_current) {
            *w = w_up;  // 증가시켰을 때 오차가 최소
        } else if (err_down &amp;lt; err_up &amp;amp;&amp;amp; err_down &amp;lt; err_current) {
            *w = w_down;  // 감소시켰을 때 오차가 최소
        }
        // 아니면 변경하지 않음 (local minimum 또는 plateau)

        // 5) 업데이트 후 예측값과 오차 재계산
        double y_pred_new  = x * (*w);
        double err_new     = squared_error(y_pred_new, y_true);

        // 6) 진행 상황 출력
        printf(&quot;Iter %3d: w = %.6f, y_pred = %.6f, error = %.6f\n&quot;,
               i, *w, y_pred_new, err_new);
    }

    printf(&quot;=== Learning Finished: final w = %.6f ===\n&quot;, *w);
}

int main(void) {
    // 학습 설정
    double input        = 0.5;     // 입력값 x
    double weight       = 0.5;     // 초기 가중치 w
    double target       = 0.8;     // 목표값 y_true
    double step_size    = 0.001;   // 가중치 증감 스텝 (learning rate)
    int    epochs       = 800;     // 반복 횟수

    // 학습 시뮬레이션 호출
    brute_force_learn(input, &amp;amp;weight, target, step_size, epochs);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 설명&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;squared_error&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예측값과 실제값의 차이를 제곱해 &lt;b&gt;항상 양수&lt;/b&gt;인 오차를 반환합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;brute_force_learn 함수 인자&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;x : 모델 입력&lt;/li&gt;
&lt;li&gt;*w: 학습 중 업데이트될 가중치 (포인터로 전달)&lt;/li&gt;
&lt;li&gt;y_true: 목표값(레이블)&lt;/li&gt;
&lt;li&gt;step: 가중치 한 스텝의 크기 (일종의 학습률 &amp;alpha;)&lt;/li&gt;
&lt;li&gt;iterations: 전체 반복 횟수(에포크 수)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;학습 루프&lt;/b&gt; (for):
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;현재 오차&lt;/b&gt;(err_current) 계산&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가중치 +step&lt;/b&gt; 일 때 오차(err_up) 계산&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가중치 &amp;ndash;step&lt;/b&gt; 일 때 오차(err_down) 계산&lt;/li&gt;
&lt;li&gt;가장 &lt;b&gt;오차가 작아지는 방향&lt;/b&gt;으로만 가중치를 업데이트&lt;/li&gt;
&lt;li&gt;업데이트 후 &lt;b&gt;새 오차&lt;/b&gt;를 계산하고, 진행 상황을 출력&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;메인 함수&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;학습 파라미터(input, weight, target, step_size, epochs)를 정의&lt;/li&gt;
&lt;li&gt;brute_force_learn 호출로 시뮬레이션 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실행 예시&lt;/h3&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;gcc -o brute_learn brute_learn.c -lm
./brute_learn
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;=== Brute‑Force Learning Start ===
Init weight = 0.500000, input x = 0.500000, target y = 0.800000

Iter   1: w = 0.501000, y_pred = 0.250500, error = 0.302150
Iter   2: w = 0.502000, y_pred = 0.251000, error = 0.301373
...
Iter 799: w = 1.598000, y_pred = 0.799000, error = 0.000001
Iter 800: w = 1.599000, y_pred = 0.799500, error = 0.000000
=== Learning Finished: final w = 1.599000 ===
&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;초기&lt;/b&gt; 예측값은 0.5&amp;times;0.5=0.25, 오차는 (0.25&amp;minus;0.8)&amp;sup2;=0.3025&amp;hellip;.&lt;/li&gt;
&lt;li&gt;반복할수록 &lt;b&gt;가중치&lt;/b&gt;가 &amp;asymp;1.6으로 이동하며, 예측값 x&amp;middot;w가 0.8에 근접하고 오차가 0에 가깝게 감소합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주의&lt;/b&gt;: 이 방식은 매우 비효율적이지만(모든 방향을 브루트 포스로 탐색),&lt;br /&gt;&lt;b&gt;&amp;ldquo;학습 = 오차를 줄이는 과정&amp;rdquo;&lt;/b&gt; 이라는 개념을 가장 직관적으로 보여 줍니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 단계로는 &lt;b&gt;경사하강법(Gradient Descent)&lt;/b&gt; 을 통해 보다 효율적으로 가중치를 업데이트하는 방법을 살펴보겠습니다!&lt;/p&gt;</description>
      <category>프로그래밍/C언어를 이용한 Deep Learning</category>
      <author>지노랩/JinoLab</author>
      <guid isPermaLink="true">https://jinolab.tistory.com/355</guid>
      <comments>https://jinolab.tistory.com/355#entry355comment</comments>
      <pubDate>Fri, 8 Aug 2025 09:37:08 +0900</pubDate>
    </item>
    <item>
      <title>12. 학습(Learn) / 단일 입력&amp;middot;단일 출력 퍼셉트론에서 오차를 줄이기 위해 가중치</title>
      <link>https://jinolab.tistory.com/354</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 용어 정리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;xx&lt;/span&gt;: 입력 값 (스칼라)&lt;/li&gt;
&lt;li&gt;&lt;span&gt;w&lt;/span&gt;: 현재 가중치&lt;/li&gt;
&lt;li&gt;&lt;span&gt;y^=x&amp;times;w &lt;/span&gt;예측값&lt;/li&gt;
&lt;li&gt;&lt;span&gt;y&lt;/span&gt;: 실제값(정답)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;순수 오차&lt;/b&gt; &lt;span&gt;&amp;delta;=y^&amp;minus;y&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;부호를 갖는 차이로, &amp;ldquo;예측이 얼마나, 어느 방향으로 벗어났는지&amp;rdquo; 알려 줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;학습률&lt;/b&gt; &lt;span&gt;&amp;alpha;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가중치 업데이트 크기를 조절하는 상수 (예: 0.01)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;델타 가중치&lt;/b&gt; &lt;span&gt;&amp;Delta;w=&amp;alpha;&amp;thinsp;&amp;delta;&amp;thinsp;x&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;업데이트&lt;/b&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;162&quot; data-origin-height=&quot;30&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lrV7D/btsPrsgi20t/b95zkp9A8d2THZOXUjRGiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lrV7D/btsPrsgi20t/b95zkp9A8d2THZOXUjRGiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lrV7D/btsPrsgi20t/b95zkp9A8d2THZOXUjRGiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlrV7D%2FbtsPrsgi20t%2Fb95zkp9A8d2THZOXUjRGiK%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;162&quot; height=&quot;30&quot; data-origin-width=&quot;162&quot; data-origin-height=&quot;30&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 의사코드(Pseudocode)&lt;/h2&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;initialize w randomly or to 0
set learning rate &amp;alpha; (e.g. 0.01)

for each training sample (x, y):
    y_pred = x * w
    &amp;delta;      = y_pred - y           # 순수 오차 (signed error)
    &amp;Delta;w     = &amp;alpha; * &amp;delta; * x            # 델타 룰
    w      = w - &amp;Delta;w               # 가중치 업데이트
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. C 언어 구현 예제&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;

/**
 * @brief 단일 퍼셉트론 한 스텝 학습 함수 (델타 룰)
 * @param x   입력 값
 * @param y   실제 값 (정답)
 * @param w   가중치 (포인터로 전달하여 업데이트)
 * @param alpha 학습률
 */
void train_step(double x, double y, double *w, double alpha) {
    double y_pred = x * (*w);         // 1) 예측값
    double delta  = y_pred - y;       // 2) 순수 오차 &amp;delta;
    double dw     = alpha * delta * x;// 3) 델타 가중치 &amp;Delta;w
    *w           -= dw;               // 4) 가중치 업데이트
}

int main(void) {
    // 학습 데이터 1개 예시 (x, y)
    double x = 25.0;
    double y = 26.0;

    // 1) 가중치 초기화
    double w = 0.0;        // 또는 작은 랜덤 값

    // 2) 학습률 설정
    double alpha = 0.01;

    // 3) 여러 에포크(epoch) 동안 반복 학습
    for (int epoch = 1; epoch &amp;lt;= 10; epoch++) {
        train_step(x, y, &amp;amp;w, alpha);
        double y_pred = x * w;
        double error  = (y_pred - y) * (y_pred - y);  // 제곱 오차
        printf(&quot;Epoch %2d: w = %.4f, y_pred = %.4f, MSE = %.4f\n&quot;,
               epoch, w, y_pred, error);
    }

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 실행 결과 예시&lt;/h2&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;gcc -o perceptron_train perceptron_train.c
./perceptron_train
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Epoch  1: w = 0.0500, y_pred = 1.2500, MSE = 615.0625
Epoch  2: w = 0.0996, y_pred = 2.4900, MSE = 569.5121
Epoch  3: w = 0.1485, y_pred = 3.7125, MSE = 499.2191
Epoch  4: w = 0.1969, y_pred = 4.9213, MSE = 436.4271
Epoch  5: w = 0.2448, y_pred = 6.1201, MSE = 393.4432
Epoch  6: w = 0.2921, y_pred = 7.3030, MSE = 356.0879
Epoch  7: w = 0.3389, y_pred = 8.4725, MSE = 311.2642
Epoch  8: w = 0.3852, y_pred = 9.6294, MSE = 271.1934
Epoch  9: w = 0.4310, y_pred = 10.7750, MSE = 238.2160
Epoch 10: w = 0.4763, y_pred = 11.9107, MSE = 203.5559
&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;Epoch별 변화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가중치 &lt;span&gt;ww&lt;/span&gt;가 점차 커지며, 예측값 &lt;span&gt;y^ &lt;/span&gt;이 실제값 &lt;span&gt;y=26&lt;/span&gt;&amp;nbsp;쪽으로 가까워집니다.&lt;/li&gt;
&lt;li&gt;MSE(제곱 오차)가 줄어드는 모습을 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&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;5. 다음 과제&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;여러 데이터 샘플&lt;/b&gt;을 배열로 저장하고, &lt;b&gt;배치 학습&lt;/b&gt; 또는 &lt;b&gt;온라인 학습&lt;/b&gt; 구현&lt;/li&gt;
&lt;li&gt;&lt;b&gt;편향(bias)&lt;/b&gt; 항 추가&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비선형 활성화 함수&lt;/b&gt; (예: 시그모이드) 적용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;다층 퍼셉트론(MLP)&lt;/b&gt; 에도 동일한 델타 룰(역전파) 적용&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예제를 통해 &amp;ldquo;학습&amp;rdquo; 단계에서 &lt;b&gt;오차를 줄이기 위해 가중치를 어떻게 업데이트하는지&lt;/b&gt; 이해하시길 바랍니다.&amp;nbsp;&lt;/p&gt;</description>
      <category>프로그래밍/C언어를 이용한 Deep Learning</category>
      <author>지노랩/JinoLab</author>
      <guid isPermaLink="true">https://jinolab.tistory.com/354</guid>
      <comments>https://jinolab.tistory.com/354#entry354comment</comments>
      <pubDate>Thu, 7 Aug 2025 09:34:44 +0900</pubDate>
    </item>
  </channel>
</rss>