<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dorahee-ee.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Mon, 11 Aug 2025 06:52:38 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dorahee-ee.log</title>
            <url>https://velog.velcdn.com/images/dorahee-ee/profile/a0fe9a5b-7297-4292-892b-0e4689abdf20/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dorahee-ee.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dorahee-ee" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[프로그래머스/SQL] 조건에 부합하는 중고거래 댓글 조회하기]]></title>
            <link>https://velog.io/@dorahee-ee/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4SQL-%EC%A1%B0%EA%B1%B4%EC%97%90-%EB%B6%80%ED%95%A9%ED%95%98%EB%8A%94-%EC%A4%91%EA%B3%A0%EA%B1%B0%EB%9E%98-%EB%8C%93%EA%B8%80-%EC%A1%B0%ED%9A%8C%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dorahee-ee/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4SQL-%EC%A1%B0%EA%B1%B4%EC%97%90-%EB%B6%80%ED%95%A9%ED%95%98%EB%8A%94-%EC%A4%91%EA%B3%A0%EA%B1%B0%EB%9E%98-%EB%8C%93%EA%B8%80-%EC%A1%B0%ED%9A%8C%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 11 Aug 2025 06:52:38 GMT</pubDate>
            <description><![CDATA[<h1 id="📍문제">📍문제</h1>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/164673">[프로그래머스] Lv.1 조건에 부합하는 중고거래 댓글 조회하기</a></p>
<p><code>USED_GOODS_BOARD</code>와 <code>USED_GOODS_REPLY</code> 테이블에서 2022년 10월에 작성된 게시글 제목, 게시글 ID, 댓글 ID, 댓글 작성자 ID, 댓글 내용, 댓글 작성일을 조회하는 SQL문을 작성해주세요. 결과는 댓글 작성일을 기준으로 오름차순 정렬해주시고, 댓글 작성일이 같다면 게시글 제목을 기준으로 오름차순 정렬해주세요.</p>
<h3 id="문제-설명">문제 설명</h3>
<p>다음은 중고거래 게시판 정보를 담은 <code>USED_GOODS_BOARD</code> 테이블과 중고거래 게시판 첨부파일 정보를 담은 <code>USED_GOODS_REPLY</code> 테이블입니다. <code>USED_GOODS_BOARD</code> 테이블은 다음과 같으며 <code>BOARD_ID</code>, <code>WRITER_ID</code>, <code>TITLE</code>, <code>CONTENTS</code>, <code>PRICE</code>, <code>CREATED_DATE</code>, <code>STATUS</code>, <code>VIEWS</code>은 게시글 ID, 작성자 ID, 게시글 제목, 게시글 내용, 가격, 작성일, 거래상태, 조회수를 의미합니다.</p>
<table>
<thead>
<tr>
<th align="center">Column name</th>
<th align="center">Type</th>
<th align="center">Nullable</th>
</tr>
</thead>
<tbody><tr>
<td align="center">BOARD_ID</td>
<td align="center">VARCHAR(5)</td>
<td align="center">FALSE</td>
</tr>
<tr>
<td align="center">WRITER_ID</td>
<td align="center">VARCHAR(50)</td>
<td align="center">FALSE</td>
</tr>
<tr>
<td align="center">TITLE</td>
<td align="center">VARCHAR(100)</td>
<td align="center">FALSE</td>
</tr>
<tr>
<td align="center">CONTENTS</td>
<td align="center">VARCHAR(1000)</td>
<td align="center">FALSE</td>
</tr>
<tr>
<td align="center">PRICE</td>
<td align="center">NUMBER</td>
<td align="center">FALSE</td>
</tr>
<tr>
<td align="center">CREATED_DATE</td>
<td align="center">DATE</td>
<td align="center">FALSE</td>
</tr>
<tr>
<td align="center">STATUS</td>
<td align="center">VARCHAR(10)</td>
<td align="center">FALSE</td>
</tr>
<tr>
<td align="center">VIEWS</td>
<td align="center">NUMBER</td>
<td align="center">FALSE</td>
</tr>
</tbody></table>
<p><code>USED_GOODS_REPLY</code> 테이블은 다음과 같으며 <code>REPLY_ID</code>, <code>BOARD_ID</code>, <code>WRITER_ID</code>, <code>CONTENTS</code>, <code>CREATED_DATE</code>는 각각 댓글 ID, 게시글 ID, 작성자 ID, 댓글 내용, 작성일을 의미합니다.</p>
<table>
<thead>
<tr>
<th align="center">Column name</th>
<th align="center">Type</th>
<th align="center">Nullable</th>
</tr>
</thead>
<tbody><tr>
<td align="center">REPLY_ID</td>
<td align="center">VARCHAR(10)</td>
<td align="center">FALSE</td>
</tr>
<tr>
<td align="center">BOARD_ID</td>
<td align="center">VARCHAR(5)</td>
<td align="center">FALSE</td>
</tr>
<tr>
<td align="center">WRITER_ID</td>
<td align="center">VARCHAR(50)</td>
<td align="center">FALSE</td>
</tr>
<tr>
<td align="center">CONTENTS</td>
<td align="center">VARCHAR(1000)</td>
<td align="center">TRUE</td>
</tr>
<tr>
<td align="center">CREATED_DATE</td>
<td align="center">DATE</td>
<td align="center">FALSE</td>
</tr>
</tbody></table>
<h3 id="출력-예시">출력 예시</h3>
<table>
<thead>
<tr>
<th align="center">TITLE</th>
<th align="center">BOARD_ID</th>
<th align="center">REPLY_ID</th>
<th align="center">WRITER_ID</th>
<th align="center">CONTENTS</th>
<th align="center">CREATED_DATE</th>
</tr>
</thead>
<tbody><tr>
<td align="center">반려견 배변패드 팝니다</td>
<td align="center">B0001</td>
<td align="center">R000000001</td>
<td align="center">s2s2123</td>
<td align="center">구매하겠습니다. 쪽지 드립니다.</td>
<td align="center">2022-10-02</td>
</tr>
<tr>
<td align="center">국내산 볶음참깨</td>
<td align="center">B0002</td>
<td align="center">R000000002</td>
<td align="center">hoho1112</td>
<td align="center">쪽지 주세요.</td>
<td align="center">2022-10-03</td>
</tr>
</tbody></table>
<hr>
<h1 id="✏️-풀이">✏️ 풀이</h1>
<pre><code class="language-sql">SELECT B.TITLE,
        B.BOARD_ID,
        R.REPLY_ID,
        R.WRITER_ID,
        R.CONTENTS,
           DATE_FORMAT(R.CREATED_DATE, &#39;%Y-%m-%d&#39;) AS CREATED_DATE
FROM USED_GOODS_BOARD AS B
JOIN USED_GOODS_REPLY AS R
ON B.BOARD_ID = R.BOARD_ID
WHERE YEAR(B.CREATED_DATE) = &#39;2022&#39; AND MONTH(B.CREATED_DATE) = &#39;10
ORDER BY R.CREATED_DATE ASC, B.TITLE ASC;</code></pre>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/74d46d7b-fe59-4bd5-9e7b-947a46d46576/image.png" alt=""></p>
<p>처음에는 <code>DATE_FORMAT</code>이라는 함수를 사용하지 않아서 출력 예시와 다르게 출력되어 계속 오답이라는 결과가 떴었다. 구글링을 해보니 <code>DATE_FORMAT</code>이라는 함수를 사용하여 출력 포맷을 맞추고 있었고, 해당 함수에 대해서 조금 더 자세히 알아보았다.</p>
<h3 id="❗️date_format">❗️<code>DATE_FORMAT()</code></h3>
<p><code>DATE_FORMAT(날짜, 형식)</code> : 날짜를 지정한 형식으로 출력</p>
<table>
<thead>
<tr>
<th align="center">구분 기호</th>
<th align="center">역할</th>
<th align="center">구분 기호</th>
<th align="center">역할</th>
</tr>
</thead>
<tbody><tr>
<td align="center">%Y</td>
<td align="center">4자리 년도</td>
<td align="center">%T</td>
<td align="center">hh:mm:SS</td>
</tr>
<tr>
<td align="center">%y</td>
<td align="center">2자리 년도</td>
<td align="center">%c</td>
<td align="center">숫자 월(한자리)</td>
</tr>
<tr>
<td align="center">%M</td>
<td align="center">긴 월(영문)</td>
<td align="center">%d</td>
<td align="center">일자(두자리)</td>
</tr>
<tr>
<td align="center">%m</td>
<td align="center">숫자 월(두 자리)</td>
<td align="center">%e</td>
<td align="center">일자(한자리)</td>
</tr>
<tr>
<td align="center">%b</td>
<td align="center">짧은 월(영문)</td>
<td align="center">%I</td>
<td align="center">시간 (12시간)</td>
</tr>
<tr>
<td align="center">%W</td>
<td align="center">긴 요일 이름(영문)</td>
<td align="center">%H</td>
<td align="center">시간(24시간)</td>
</tr>
<tr>
<td align="center">%a</td>
<td align="center">짧은 요일 이름(영문)</td>
<td align="center">%r</td>
<td align="center">hh:mm:ss AM, PM</td>
</tr>
<tr>
<td align="center">%i</td>
<td align="center">분</td>
<td align="center">%S</td>
<td align="center">초</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[PyTorch] 신경망 모델 정의하기]]></title>
            <link>https://velog.io/@dorahee-ee/PyTorch-%EC%8B%A0%EA%B2%BD%EB%A7%9D-%EB%AA%A8%EB%8D%B8-%EC%A0%95%EC%9D%98%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dorahee-ee/PyTorch-%EC%8B%A0%EA%B2%BD%EB%A7%9D-%EB%AA%A8%EB%8D%B8-%EC%A0%95%EC%9D%98%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 05 Jan 2025 15:14:33 GMT</pubDate>
            <description><![CDATA[<h2 id="pytorch-모델의-기본-구조">PyTorch 모델의 기본 구조</h2>
<p>PyTorch로 설계하는 신경망은 기본적으로 다음과 같은 구조를 갖는다.</p>
<pre><code class="language-python">import torch.nn as nn
import torch.nn.functional as F

class Model_Name(nn.Module):
    def __init__(self):

        super(Model_Name, self).__init__()
        self.module1 = ...
        self.module2 = ...

        &quot;&quot;&quot;
        ex)
        self.conv1 = nn.Conv2d(1, 20, 5)
        self.conv2 = nn.Conv2d(20, 20, 5)
        &quot;&quot;&quot;

    def forward(self, x):

        x = some_function1(x)
        x = some_function2(x)

        &quot;&quot;&quot;
        ex)
        x = F.relu(self.conv1(x))
        x = F.relu(self.conv2(x))
        &quot;&quot;&quot;
        return x

model = Model_Name()</code></pre>
<p>PyTorch 모델로 쓰기 위해선 다음 두 가지 조건을 따라야한다. 내장된 모델(nn.Linear등)도 이를 만족한다. </p>
<ol>
<li><code>torch.nn.Module</code>을 상속해야한다. </li>
</ol>
<ul>
<li>interitance: 상속; 어떤 클래스를 만들 때 다른 클래스의 기능을 그대로 가지고오는 것.</li>
</ul>
<ol start="2">
<li><code>__init()__</code>과 <code>forward()</code>를 override 해야한다.</li>
</ol>
<ul>
<li>override: 재정의; torch.nn.Module(부모클래스)에서 정의한 메소드를 자식클래스에서 변경하는 것.</li>
<li><code>__init()__</code>에서는 모델에서 사용될 module(nn.Linear, nn.Conv2d), activation function(nn.functional.relu, nn.functional.sigmoid)등을 정의한다. </li>
<li><code>forward()</code>에서는 모델에서 실행되어야하는 계산을 정의한다. backward 계산은 backward()를 이용하면 PyTorch가 알아서 해주니까 forward()만 정의해주면 된다. input을 넣어서 어떤 계산을 진행하하여 output이 나올지를 정의해준다고 이해하면 됨. </li>
</ul>
<h2 id="클래스-상속-class-inheritance">클래스 상속 (Class inheritance)</h2>
<p>PyTorch 모델 구조를 보다가 클래스 상속에 대한 궁금증이 생겼다. <a href="https://scored-a-belter.tistory.com/7">[Python] 클래스 상속(Class inheritance) 그리고 Pytorch 모델에서의 해석</a>을 참고하여 작성하였다.</p>
<pre><code class="language-python">class Person:
  def __init__(self, fname, lname): # init으로 fname, lanme을 받아 firstname, lastname에 각각 저장함.
    self.firstname = fname
    self.lastname = lname

  def printname(self): # 저장한 firstname, lastname을 출력함.
    print(self.firstname, self.lastname)


x = Person(&quot;John&quot;, &quot;Doe&quot;)
x.printname()</code></pre>
<h3 id="parent-클래스-상속-받기">Parent 클래스 상속 받기</h3>
<pre><code class="language-python">class Student(Person):
  pass</code></pre>
<p>Parent class인 Person에서 모든 것을 상속받았고 그 외의 기능은 없으므로(pass) Person과 동일하게 사용할 수 있다.</p>
<pre><code class="language-python">y = Student(&quot;HH&quot;, &quot;jj&quot;)
y.printname()</code></pre>
<pre><code># output:
HH jj</code></pre><h3 id="init-함수-대체하기-override"><strong>init</strong> 함수 대체하기 (override)</h3>
<p>Parent class의 <code>__init__</code> 함수가 마음에 안든다면 대체할 수 있다. 그냥 원래 <code>__init__</code> 함수를 선언하듯 하면 된다.</p>
<pre><code class="language-python">class Student(Person):
  def __init__(self, fname, lname):
    print(fname, lname)

y = Student(&quot;hello&quot;, 100)</code></pre>
<pre><code>output:
hello 100</code></pre><p><code>__init__</code> 함수만 바뀐 것이기 때문에 Parent class의 다른 기능들은 사용할 수 있다.</p>
<p><code>y.printname()</code>은 원래는 사용가능하지만, 이 경우 parent class에서 <code>self.firstname</code>, <code>self.lastname</code>으로 저장을 했었는데 그러지 않았으므로 에러를 출력할 것이다.</p>
<h3 id="super-함수로-상속하기"><code>super()</code> 함수로 상속하기</h3>
<p>개인적으로 이 부분이 가장 궁금했던 부분이다.</p>
<p>Parent class의 <code>__init__</code>을 유지하고 싶다면 아래처럼 작성하면 된다.</p>
<pre><code class="language-python">class Student(Person):
  def __init__(self, fname, lname):
    Person.__init__(self, fname, lname)</code></pre>
<p>하지만 굳이 이름을 넣지 않고도 <code>super()</code>를 통해서도 가능하다.</p>
<pre><code class="language-python">class Student(Person):
  def __init__(self, fname, lname):
    super().__init__(fname, lname) # __init__에 &#39;self&#39; 인자가 들어가지 않는다.</code></pre>
<p><strong>다만 <code>super()</code>를 사용 시 <code>__init__</code>에 <code>self</code> 인자를 넣으면 안된다.</strong></p>
<p>처음에는 &quot;어 근데 유지를 하고싶은데 굳이 선언을 해줘야 하나? 위에 pass처럼 그냥 두면 되는거 아닌가?&quot; 라는 생각이 들었는데 아래를 보고 의문이 풀렸다.</p>
<pre><code class="language-python">class Student(Person):
  def __init__(self, fname, lname, year):
    super().__init__(fname, lname)
    self.graduationyear = year

x = Student(&quot;Mike&quot;, &quot;Olsen&quot;, 2019)</code></pre>
<p>이렇게 코드를 짜면 Person의 <code>__init__</code>을 받으면서 <code>self.graduationyear</code>까지 init에 추가시킬 수 있다. 
말하자면 <code>Person.__init__</code> + 내가 추가하고 싶은 <code>__init__</code></p>
<p>Person의 <code>__init__</code>에서 <code>self.firstname</code>, <code>self.lastname</code>이 저장되고 나의 <code>__init__</code>에서 <code>self.graduationyear</code>가 저장되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ML] Decision Tree (의사결정나무)]]></title>
            <link>https://velog.io/@dorahee-ee/ML-Decision-Tree-%EC%9D%98%EC%82%AC%EA%B2%B0%EC%A0%95%EB%82%98%EB%AC%B4</link>
            <guid>https://velog.io/@dorahee-ee/ML-Decision-Tree-%EC%9D%98%EC%82%AC%EA%B2%B0%EC%A0%95%EB%82%98%EB%AC%B4</guid>
            <pubDate>Fri, 13 Dec 2024 02:25:04 GMT</pubDate>
            <description><![CDATA[<h1 id="decision-tree란">Decision Tree란?</h1>
<p>: <strong>의사결정나무(decision tree)</strong>는 여러 가지 규칙을 순차적으로 적용하면서 독립 변수 공간을 분할하는 분류 모형이다. 분류(classification)와 회귀(regression)에 모두 사용될 수 있기 때문에 <strong>CART(Classification And Regression Tree)</strong>라고도 한다.</p>
<h2 id="작동-원리">작동 원리</h2>
<p>: 의사결정 나무의 핵심 원리는 <code>불순도(impurity)</code>를 최소화하는 방향으로 데이터를 분할하는 것이다. 불순도는 일반적으로 지니 계수(Gini Index), 엔트로피(Entropy), 정보 이득(Information Gain) 등을 사용해 측정한다.</p>
<h3 id="불순도impurity">불순도(Impurity)</h3>
<p>: 각 노드에서 클래스의 혼합 정도이다. 불순도가 낮을수록 해당 노드는 한 클래스의 데이터로 잘 분류된 상태를 의미한다.
의사결정 나무는 불순도를 최소화하는 방향으로 학습을 진행하게 된다. </p>
<h3 id="지니계수gini">지니계수(Gini)</h3>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/b0ebe6aa-3df6-4bac-9173-127dd4a9e185/image.png" alt=""></p>
<p>: 데이터의 불순도를 측정하는 방법으로 0에서 1사이의 값을 가진다. 0은 완벽하게 분류된 상태(하나의 클래스만 존재), 1은 모든 클래스가 동등하게 분포된 상태를 의미한다. </p>
<h3 id="엔트로피entropy">엔트로피(Entropy)</h3>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/c030c121-7365-494b-9bc9-cc3736bfe606/image.png" alt=""></p>
<p>: 엔트로피도 불순도를 측정하는 방법 중 하나로, 정보의 불확실성을 측정한다. 엔트로피가 높을수록 정보의 불확실성이 높아진다. 엔트로피는 클래스의 비율이 균등할수록 높은 값을 가진다. 따라서 의사결정나무는 엔트로피를 최소화하는 방향으로 분할한다. </p>
<h3 id="정보-이득information-gain">정보 이득(Information Gain)</h3>
<p>정보이득 = 부모노드의 불순도 - 자식노드의 불순도들의 가중평균</p>
<p>: 불순도를 감소시키는 정도를 측정하는 지표로 부모노드와 자식노드의 불순도의 차이를 의미한다. 즉, 어떤 특성을 선택했을 때 얻는 정보의 양을 나타낸다. 정보 이득이 큰 특성을 기준으로 데이터를 분할하게 된다.</p>
<blockquote>
<p>정보이득의 최대화 -&gt; 불순도의 감소 -&gt; 엔트로피의 감소</p>
</blockquote>
<h2 id="가지치기pruning">가지치기(Pruning)</h2>
<p>: 과적합을 방지하기 위한 방법 중 하나이다. Full Tree는 모든 끝 마디에서의 순도가 100%인 상태입니다. 분기가 너무 많아져서 Overfitting이 일어나기 쉽기 때문에 적절한 수준에서 끝 노드를 결합해주는 <strong>가지치기</strong>가 필요하다. </p>
<h2 id="특성-중요도feature-importance">특성 중요도(Feature Importance)</h2>
<p>: 각 특성이 얼마나 중요한 역할을 하는지를 측정하는 지표이다. 이는 해당 특성에 의한 분할이 불순도를 얼마나 감소시키는지에 따라 결정된다. 특성 중요도의 합은 항상 1이며, 각 특성의 중요도는 0과 1사이의 값을 갖는다. 불순도를 가장 크게 감소시키는 변수의 중요도가 가장 크다. </p>
<h2 id="python-예제-코드">Python 예제 코드</h2>
<p><code>scikit-learn</code>의 DecisionTreeClassifier를 사용하였다. <code>scikit-learn</code>에서는 CART 알고리즘을 사용하는데 이경우는 이진트리로만 분기한다. </p>
<pre><code class="language-python">from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier

iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2, random_state=42)

clf = DecisionTreeClassifier(criterion=&#39;gini&#39;, max_depth=None, random_state=42)
clf.fit(X_train, y_train)</code></pre>
<pre><code class="language-python">import matplotlib.pyplot as plt
from sklearn.tree import plot_tree
plt.figure(figsize=(25,10))
plot_tree(clf, filled=True, fontsize = 14)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/e77fe8e0-dd4b-4278-8f6f-7aca8fc5c2ad/image.png" alt=""></p>
<h3 id="decisiontreeclassifier-하이퍼파라미터">DecisionTreeClassifier 하이퍼파라미터</h3>
<ul>
<li><code>criterion</code> : &#39;gini&#39; 또는 &#39;entropy&#39;. 기본값은 &#39;gini&#39;. 이 매개변수는 불순도를 측정하는 기준을 결정합니다. &#39;gini&#39;는 지니 불순도를, &#39;entropy&#39;는 엔트로피를 의미합니다.</li>
<li><code>splitter</code> : &#39;best&#39; 또는 &#39;random&#39;. 기본값은 &#39;best&#39;. &#39;best&#39;는 가장 좋은 분할을 찾고, &#39;random&#39;은 무작위로 분할합니다</li>
<li><code>max_depth</code> : 트리의 최대 깊이를 결정합니다. 이 매개변수를 통해 과적합을 방지할 수 있습니다. None으로 설정하면, 모든 잎이 순수해질 때까지 트리가 성장합니다.</li>
<li><code>min_samples_split</code> : 노드를 분할하기 위해 필요한 최소 샘플 수를 결정합니다. 숫자로 지정할 수도 있고, 전체 샘플 수에 대한 비율로 지정할 수도 있습니다.</li>
<li><code>min_samples_leaf</code> : 리프 노드에 있어야 하는 최소 샘플 수를 결정합니다. 숫자로 지정할 수도 있고, 전체 샘플 수에 대한 비율로 지정할 수도 있습니다.</li>
<li><code>min_weight_fraction_leaf</code> : 가중치가 부여된 전체 샘플 수에 대한 비율로, 리프 노드에 있어야 하는 최소 샘플 수를 결정합니다.</li>
<li><code>max_features</code> : 각 노드에서 분할에 사용할 특성의 최대 수를 결정합니다.</li>
<li><code>random_state</code> : 내부적으로 사용되는 난수 생성기의 시드입니다. 이 값을 고정하면, 같은 조건에서 같은 결과를 얻을 수 있습니다.</li>
<li><code>max_leaf_nodes</code> : 리프 노드의 최대 수를 결정합니다. None일 경우 제한이 없습니다.</li>
<li><code>min_impurity_decrease</code> : 이 값보다 불순도 감소가 큰 분할만 고려됩니다.</li>
<li><code>class_weight</code> : 클래스 가중치를 지정합니다. 주로 불균형 데이터셋에 사용됩니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[PyTorch] 텐서(Tensor)]]></title>
            <link>https://velog.io/@dorahee-ee/PyTorch-%ED%85%90%EC%84%9CTensor</link>
            <guid>https://velog.io/@dorahee-ee/PyTorch-%ED%85%90%EC%84%9CTensor</guid>
            <pubDate>Thu, 17 Oct 2024 07:22:01 GMT</pubDate>
            <description><![CDATA[<h1 id="tensor">Tensor</h1>
<ul>
<li>Tensor는 numpy의 ndarray와 유사하며, GPU를 사용한 연산 가속이 가능하다.</li>
<li>PyTorch에서는 텐서를 사용하여 모델의 입/출력뿐만 아니라 모델의 매개변수를 부호화하고 GPU를 활용해 연산을 가속화할 수 있다.</li>
<li>NumPy와 공통점은 수학 계산, 선형 대수 연산을 비롯해 전치(Transposing), 인덱싱(Indexing), 슬라이싱(slicing), 임의 샘플링(random sampling) 등 다양한 텐서 연산을 진행할 수 있다.</li>
<li>NumPy와의 차이점은 CPU에서 사용하는 텐서와 GPU에서 사용하는 텐서의 선언 방식의 차이가 있다.</li>
<li>GPU 가속(GPU Acceleration)을 적용할 수 있으므로 CPU 텐서와 GPU 텐서로 나눠지고, 각각의 텐서를 상호 변환하거나 GPU 사용 유/무를 설정한다.</li>
</ul>
<hr>
<h2 id="텐서-속성-attribute">텐서 속성 (Attribute)</h2>
<p>텐서의 속성은 텐서의 모양(shape), 자료형(datatype) 그리고 어느 장치(device)에 저장되는지 등을 말한다.</p>
<pre><code class="language-python">tensor = torch.rand(3, 4)

print(f&quot;Shape of tensor: {tensor.shape}&quot;)
print(f&quot;Datatype of tensor: {tensor.dtype}&quot;)
print(f&quot;Device tensor is stored on: {tensor.device}&quot;)</code></pre>
<pre><code># OUTPUT
Shape of tensor: torch.Size([3, 4])
Datatype of tensor: torch.float32
Device tensor is stored on: cpu</code></pre><hr>
<h2 id="텐서-초기화-initialization">텐서 초기화 (Initialization)</h2>
<h3 id="데이터로부터-직접-생성하기">데이터로부터 직접 생성하기</h3>
<pre><code class="language-python">data = [[1, 2], [3, 4]]
x_data = torch.tensor(data)</code></pre>
<h3 id="numpy-배열로부터-생성하기">NumPy 배열로부터 생성하기</h3>
<pre><code class="language-python">np_array = np.array(data)
x_np = torch.from_numpy(np_array)</code></pre>
<h3 id="다른-텐서로부터-생성하기">다른 텐서로부터 생성하기</h3>
<p>명시적으로 재정의하지 않는다면, 인자로 주어진 텐서의 속성(모양(shape), 자료형(datatype))을 유지한다.</p>
<pre><code class="language-python">x_ones = torch.ones_like(x_data) # x_data 속성 유지
print(f&quot;Ones Tensor: \n {x_ones} \n&quot;)

x_rand = torch.rand_like(x_data, dtype=torch.float) # x_data 속성 재정의
print(f&quot;Random Tensor: \n {x_rand} \n&quot;)

# OUPUT
Ones Tensor:
 tensor([[1, 1],
        [1, 1]])

Random Tensor:
 tensor([[0.8823, 0.9150],
        [0.3829, 0.9593]])</code></pre>
<h3 id="무작위random-또는-상수constant-값-사용하기">무작위(random) 또는 상수(constant) 값 사용하기</h3>
<p><code>shape</code>은 텐서의 차원(dimension)을 나타내는 튜플(tuple)로, 아래 함수들에서는 출력 텐서의 차원을 결정한다.</p>
<pre><code class="language-python">shape = (2, 3,)
rand_tensor = torch.rand(shape)
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)

print(f&quot;Random Tensor: \n {rand_tensor} \n&quot;)
print(f&quot;Ones Tensor: \n {ones_tensor} \n&quot;)
print(f&quot;Zeros Tensor: \n {zeros_tensor}&quot;)

# OUPUT
Random Tensor:
 tensor([[0.3904, 0.6009, 0.2566],
        [0.7936, 0.9408, 0.1332]])

Ones Tensor:
 tensor([[1., 1., 1.],
        [1., 1., 1.]])

Zeros Tensor:
 tensor([[0., 0., 0.],
        [0., 0., 0.]])</code></pre>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ML] Precision(정밀도) & Recall(재현율)]]></title>
            <link>https://velog.io/@dorahee-ee/ML-Precision%EC%A0%95%EB%B0%80%EB%8F%84-Recall%EC%9E%AC%ED%98%84%EC%9C%A8</link>
            <guid>https://velog.io/@dorahee-ee/ML-Precision%EC%A0%95%EB%B0%80%EB%8F%84-Recall%EC%9E%AC%ED%98%84%EC%9C%A8</guid>
            <pubDate>Thu, 19 Sep 2024 04:39:03 GMT</pubDate>
            <description><![CDATA[<h2 id="confusion-matrix">Confusion Matrix</h2>
<p>Confusion Matrix는 실제 클래스와 예측된 클래스의 매칭을 이용하여 분류 모델을 평가하는 도구이다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/e26ba9e6-cf57-42c5-9d95-65330e745beb/image.png" alt=""></p>
<ul>
<li>True Positive(TP) : 실제 True인 정답을 True라고 예측 (정답)</li>
<li>False Positive(FP) : 실제 False인 정답을 True라고 예측 (오답)</li>
<li>False Negative(FN) : 실제 True인 정답을 False라고 예측 (오답)</li>
<li>True Negative(TN) : 실제 False인 정답을 False라고 예측 (정답)</li>
</ul>
<h2 id="accuracy-정확도">Accuracy (정확도)</h2>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/803b606d-eb95-4645-a263-951e8cd242e1/image.png" alt=""></p>
<p><code>정확도</code>란 판별한 전체 샘플 중 TP와 TN의 비율이다. 분류 모델을 평가하기에 가장 단순한 지표이지만, 불균형한 클래스를 가진 데이터셋을 평가하기는 어렵다는 단점이 있다. 또한, Domain의 편중(Bias) 문제가 발생할 수 있다.
<em>예를 들어, 더운 지역에 눈이오는 날 수를 예측한다고 할 때, 모두 &#39;오지 않는다&#39;라고만 분류기를 만들어도 분류기는 상당히 높은 정확성을 보일 것이다. 이처럼 data자체의 domain이 불균형할 경우 문제가 생길 수 있다.</em></p>
<h2 id="precision-정밀도">Precision (정밀도)</h2>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/eaff1a74-76a2-4680-983c-0c2b247bc9fa/image.png" alt=""></p>
<p><code>정밀도</code>란 분류 모델이 Positive로 판정한 것 중, 실제로 Positive인 샘플의 비율이다.
Positive 정답률이라고도 불리며, Positive라고 분류된 결과가 얼마나 정확한지를 나타낸다.</p>
<h2 id="recall-재현율">Recall (재현율)</h2>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/724b19f4-4b37-4973-9032-4b43d26cb2a4/image.png" alt=""></p>
<p><code>재현율</code>이란 실제 Positive 샘플 중 분류 모델이 Positive로 판정한 비율이다.
민감도라고도 불리며, 분류 모델이 실제 Positive 클래스를 얼마나 빠지지 않고 잡아내는지를 나타낸다.</p>
<h2 id="precision---recall-curve">Precision - Recall Curve</h2>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/b272be68-dff7-4f85-9b4e-eb20ba7e1af6/image.png" alt=""></p>
<p><code>Precision</code>과 <code>Recall</code>은 <em>trade-off</em> 관계에 있어 어느 한쪽이 올라가면 다른 한쪽이 떨어진다. 분류 모델의 decision threshold를 통해 trade-off를 조절할 수 있다.
<code>Decision Threshold</code>란 분류 모델의 결과인 [0, 1] 사이의 값을 positive 또는 Negative로 결정하는 경계를 의미한다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/c72302e4-a168-4330-a853-7a9b0f1febcf/image.png" alt=""></p>
<p>Precision-Recall Curve 위의 한 점은 특정 threshold에 해당하는 Precision과 Recall 값을 의미한다. 또한, 오른쪽 그림과 같이 Precision과 Recall 값을 Threshold 변화에 따른 그래프로 나타낼 수도 있다.  </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DL] Cross Entropy (크로스 엔트로피)]]></title>
            <link>https://velog.io/@dorahee-ee/DL-Cross-Entropy</link>
            <guid>https://velog.io/@dorahee-ee/DL-Cross-Entropy</guid>
            <pubDate>Mon, 08 Jul 2024 08:08:22 GMT</pubDate>
            <description><![CDATA[<p>신박Ai님의 <a href="https://www.youtube.com/watch?v=vAaReyHMfY8">[Deep Learning 101] Cross Entropy 크로스엔트로피 손실함수를 알아보자</a> 강의를 정리하였습니다.</p>
<hr>
<h2 id="정보-information-놀람-suprisal">정보 (Information, 놀람, suprisal)</h2>
<h3 id="확률">확률</h3>
<p>: 가능한 모든 상황에서 어떤 이벤트가 발생하는 빈도수</p>
<p>99개의 파란 종이와 1개의 빨간 종이, 총 100개의 종이로 제비뽑기를 한다고 가정하면,
파란 종이를 뽑을 확률은 99%이고, 빨간 종이를 뽑을 확률은 1%이다.</p>
<p>이때, 사람들은 파란 종이를 뽑으면 시큰둥하겠지만, 빨간 종이를 뽑는다면 매우 놀랄 것이다. 즉, 확률이 높으면 그 사건이 발생해도 별로 놀라지 않고, 확률이 낮으면 그 사건이 발생했을 때 많이 놀라게 된다. </p>
<p><em>즉, <code>확률</code>과 <code>놀람</code>은 무언가 서로 반비례의 개념이다.</em></p>
<p>놀람을 수학적으로 표현하자면, 확률을 $p(x)$로 할 경우 놀람은 확률의 역수, $\frac{1}{p(x)}$로 표현할 수 있다.</p>
<p>실제 정보이론에서 정의하는 놀람의 공식은 $log(\frac{1}{p(x)})$이다.
정보를 효율적으로 전달하기 위한 방법으로 log를 사용하였다.</p>
<center><img src="https://velog.velcdn.com/images/dorahee-ee/post/f550d916-1fcb-43a8-a760-05318303dbf1/image.png" width="70%" height="70%"></center>

<p>정보이론에서 정보란 일종의 의외성, 놀람을 객관적인 수치로 표현한 것을 말한다. 
일종의 <code>놀람도</code>라고 생각하면 된다. 보통 <code>놀람</code>이라는 것은 주관적이고 심리적인 현상이기 때문에, 정보이론에서는 보다 객관적인 지표의 의미가 있는 <code>정보</code>라는 단어를 사용한다.</p>
<center><img src="https://velog.velcdn.com/images/dorahee-ee/post/b4a8f646-b815-42d4-8c68-f9e0dba4e8b3/image.png" width="70%" height="70%"></center>

<blockquote>
<p>하지만 여기서는 그 이름이 정보가 되었든, 놀람이 되었든, _그 의미가 확률에 반비례하는 것_을 기억하면 된다!</p>
</blockquote>
<hr>
<h2 id="기댓값-expectation-value">기댓값 (Expectation value)</h2>
<p>: 어떤 값에 확률을 곱한 값</p>
<p>신박 FC와 AI FC가 축구 경기를 한다고 가정하면,</p>
<p>기본적으로 축구 경기의 승패는 각 선수의 기량에 따라 좌우가 된다.
각 선수들의 기량의 평균을 구해보았을 때, 객관적인 전력면에서는 신박FC가 높다고 볼 수 있다.</p>
<center><img src="https://velog.velcdn.com/images/dorahee-ee/post/f0f9e1e9-d35b-46fe-984c-ae81c002dd3c/image.png" width="70%" height="70%"></center>

<p>그러나, 객관적 전력이 높다고 신박FC가 무조건 이기는 것은 아니다.
컨디션에 따라 제 기량을 100% 발휘하지 못하는 경우도 있기 때문에 각각의 기량에 제 기량을 발휘할 확률을 곱해보면 AI FC의 값이 더 큰 것을 확인할 수 있다.
이렇게 각각의 기량에 제 기량을 발휘할 확률을 곱한 것은 각 팀의 그날 예상되는 기대 전력이라고 볼 수 있다.
한 선수의 오늘 예상 실력 = 객관적인 선수의 기량 x 그 기량이 오늘 발휘될 확률</p>
<center><img src="https://velog.velcdn.com/images/dorahee-ee/post/3fb75afe-282b-4e71-9461-62325c0320ee/image.png" width="70%" height="70%"></center>

<p>이렇게 어떤 값에 확률을 곱한 값을 기댓값이라고 부른다.</p>
<center><img src="https://velog.velcdn.com/images/dorahee-ee/post/60e95f13-22d7-4bf2-b313-a672e8e80f39/image.png" width="70%" height="70%"></center>

<hr>
<h2 id="엔트로피-entropy">엔트로피 (Entropy)</h2>
<p>: 놀람의 예상 값 (즉, 기대놀람도) </p>
<p>위에서 살펴본 기댓값 공식에서 $x$(객관적 전력, 수치)대신 <code>놀람도</code>를 넣으면 엔트로피 공식이 된다.</p>
<center><img src="https://velog.velcdn.com/images/dorahee-ee/post/a20a8d1e-c53c-425f-8fab-e2886cc3ff25/image.png" width="70%" height="70%"></center>

<p>위에서 예시를 들었던 축구 경기로 다시 가정을 해보면,
다음과 같이 예상되는 기대 전력의 값이 동일할 경우에는 어떤 팀의 승리 확률이 높은지 예상하기 쉽지 않다.</p>
<center><img src="https://velog.velcdn.com/images/dorahee-ee/post/d4c09cac-7594-4844-9331-b28fbfbcbea5/image.png" width="70%" height="70%"></center>

<p>이런 경우, 엔트로피를 이용해볼 수 있다.
예를 들어, 확률이 0.9 또는 0.1인 선수들은 안정적으로 잘하거나 못하는 선수라는 것을 예측해볼 수 있다. 결국 예측 가능성이 더 높다는 뜻이다.</p>
<p>이렇듯 예측 가능성을 계산할 때 엔트로피가 이용된다. </p>
<p>엔트로피는 어떤 사건들을 예상치 못한 경우가 많아질 때 높아지는 경향이 있다.</p>
<center><img src="https://velog.velcdn.com/images/dorahee-ee/post/135644d3-6d05-473a-ad52-8a50e408b46e/image.png" width="70%" height="70%"></center>

<hr>
<h2 id="크로스-엔트로피-cross-entropy">크로스 엔트로피 (cross entropy)</h2>
<p>크로스 엔트로피의 공식은 엔트로피 공식에서 하나만 바꾸면 된다.
즉, $p(x)$의 확률에 $q(x)$의 놀람도를 곱하는 것이다.</p>
<center><img src="https://velog.velcdn.com/images/dorahee-ee/post/11087ec3-c7f2-413d-8008-75c1f5513e2a/image.png" width="70%" height="70%"></center>

<p>만약 주어진 정답이 있고, 신경망을 통해 도출되는 출력 값이 이와 같다면, 신경망은 불편함을 느낀다. </p>
<p>크로스 엔트로피 공식을 통해 다음과 같이 loss를 계산할 수 있고, 이 loss가 작아지는 방향으로 가중치를 업데이트하게 된다.</p>
<center><img src="https://velog.velcdn.com/images/dorahee-ee/post/5c542d2c-7955-4af4-aefc-98dca8e16702/image.png" width="70%" height="70%"></center>

<h3 id="크로스-엔트로피가-mse보다-더-좋은-이유는">크로스 엔트로피가 MSE보다 더 좋은 이유는?</h3>
<p>물론 MSE는 좋은 손실 함수이고, 직관적이고 쓰기 쉬운 함수이다.</p>
<center><img src="https://velog.velcdn.com/images/dorahee-ee/post/73874743-3db2-4611-8d91-8d742d0f4751/image.png" width="70%" height="70%"></center>

<p>_그러나, 크로스 엔트로피가 더 좋은 이유는 MSE보다 손실을 더 잘 보여주기 때문이다. _</p>
<p>예측값이 실제 값에 비해 멀면 멀수록 크로스 엔트로피의 손실값이 MSE보다 크고, 기울기도 커서 크로스 엔트로피가 MSE에 비해 효율적으로 손실을 최소화 할 수 있다.</p>
<center><img src="https://velog.velcdn.com/images/dorahee-ee/post/4af3289c-43ff-4848-9548-27610e0a3f45/image.png" width="70%" height="70%"></center>


]]></description>
        </item>
        <item>
            <title><![CDATA[[Biomedical] ECG]]></title>
            <link>https://velog.io/@dorahee-ee/ECG</link>
            <guid>https://velog.io/@dorahee-ee/ECG</guid>
            <pubDate>Thu, 09 May 2024 04:23:02 GMT</pubDate>
            <description><![CDATA[<h2 id="심전도란">심전도란?</h2>
<p>ECG(심전도)란 심박동과 관련되어 나타나는 전위변화를 나타내는 심장의 전기적 그림을 말한다. 즉, 심근의 탈분극과 재분극에 의하여 일어나는 전기적 흥분 상태가 그래프상에 기록된 것이다.
<img src="https://velog.velcdn.com/images/dorahee-ee/post/cd78e188-9dd0-44a2-857e-023dabd30e42/image.png" width="50%" height="50%"></p>
<p>위 그림은 심장박동 1회에 해당하는 ECG 그래프이다.
첫 시작점은 P wave이고, 이후 QRS 구간과 T wave 및 U wave 구간이 나타난다.
심전도의 파형은 심장의 전기적 활동으로 인해 나타나게 되는데, 기본적으로 <strong>P wave, QRS complex, T wave</strong> 이렇게 3가지의 파형이 있다. 그리고 구간(또는 간격)으로는 <strong>PR interval, ST segment, QT interval</strong>이 있다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/04743894-3e26-46ab-9e50-f36cab5279a2/image.png" alt=""></p>
<h2 id="파형-및-구간">파형 및 구간</h2>
<h3 id="p-wave">P wave</h3>
<p>: 심방 탈분극시 나타나는 파형으로 오른쪽에서 왼쪽, 그리고 아랫 방향으로의 전기적 자극의 전도로 인해 발생한다.</p>
<ul>
<li>QRS Complex 앞에 위치</li>
<li>심전도 그래프에서 2~3mm 정도</li>
<li>0.006~0.12초 동안 지속</li>
<li>둥글고 살짝 튀어나와 있는 모양</li>
</ul>
<h3 id="qts-complex">QTS complex</h3>
<p>: 심실의 탈분극을 의미하며 전기 자극이 방전될 때 나타나는 파형으로, 위로 솟은 모양이다. 이는 AV junction(심방 연결지점)에서부터 Purkinje Fibres(푸르킨예 섬유)까지의 정상적인 전도 과정을 의미한다. </p>
<ul>
<li>PR interval 다음으로 형성</li>
<li>5~30mm 정도의 높이 : 너무 높으면 심근 비대증 의심 / 너무 짧으면 심부전, 비만</li>
<li>0.08~0.12초 동안 지속 : 이 구간이 너무 넓으면 전기전도 지연 및 차단을 의미 (notched QRS complex 모양이면 심장에서 대체 경로가 있음을 의미)</li>
</ul>
<h3 id="st-segment">ST segment</h3>
<p>: 심실 전도의 마지막, 재탈분극의 시작에 발생한다.</p>
<ul>
<li>S파부터 T파의 시작점까지 연장된다.</li>
<li>isoelectric line이다.</li>
<li>ST segment depression(구간이 아래로 쳐져 있음)이 보일 경우에는 심근 허열증, 심실 비대증을 의심해 볼 수 있다.</li>
<li>widespread saddle-shaped ST segment elevation(넓게 퍼지거나 움푹 파임)이 나타날 경우 좌심실 동맥류, 협심증을 의심해볼 수 있다.</li>
</ul>
<h3 id="t-wave">T wave</h3>
<p>: 심실 탈분극 시 나타나는 파형</p>
<ul>
<li>S파 다음으로 나타난다.</li>
<li>둥글고 부드러운 모형이고, 보통 위로 솟아있다.</li>
<li>QRS complex의 절반 이상 높이를 넘기지 않는다.</li>
<li>비정상적으로 높게 나타날 경우, 고칼륨형증, 급성 심근경색을 의심해 볼 수 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS231N] Lecture 5 | Convolutional Neural Networks]]></title>
            <link>https://velog.io/@dorahee-ee/CS231N-Convolutional-Neural-Networks</link>
            <guid>https://velog.io/@dorahee-ee/CS231N-Convolutional-Neural-Networks</guid>
            <pubDate>Fri, 29 Mar 2024 07:13:10 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.youtube.com/watch?v=d14TUNcbn1k&amp;list=PL3FW7Lu3i5JvHM8ljYj-zLfQRF3EO8sYv&amp;index=5">CS231N - Lecture 5 | Convolutional Neural Networks</a></p>
<h1 id="convolutional-neural-networks-cnn">Convolutional Neural Networks (CNN)</h1>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/d46c5eb8-d4ac-4fc2-9c4a-ba982aa13690/image.png" alt=""></p>
<h2 id="fully-conected-layer">Fully Conected Layer</h2>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/2d91d7a8-3dd4-4e77-8452-61d658fab324/image.png" alt=""></p>
<p>지난 강의에서 배웠던 Fully Connected Layer에 대해 다시 한번 짚고 넘어가자.
32 x 32 x 3의 이미지가 있으면, 그것을 3072 x 1의 크기로 변환시킨 것을 input값으로 하고, $Wx$ 값들은 (클래스 개수인) 10 x 3072로 만들어 둔 뒤에, input과 Weight를 곱하여 10개의 output 값들을 만들어 내는 layer가 <code>Fully connected layer</code>이다.</p>
<hr>
<h2 id="convolutional-layer">Convolutional Layer</h2>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/fb13ec5e-22b6-4ff1-9354-5e4903643ba6/image.png" alt=""></p>
<p><strong>Convolve</strong>란 입력 이미지에 filter를 슬라이딩해서 내적을 구하는 것을 말한다.
<strong>Weight</strong>는 위의 자그마한 filter가 될 것이다. <code>filter</code>의 경우 가로, 세로의 크기는 input보다 크지만 않으면 되지만 깊이는 <em>무조건 같아야한다</em>.</p>
<h4 id="fully-connected-layer-vs-convolutional-layer">Fully Connected Layer vs Convolutional Layer</h4>
<p>$\rightarrow$ Convolutional Layer에서는 Fully Connected Layer와 달리 input값을 stretch하지 않고 원형을 보존한다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/81f87b27-93ef-48c5-977c-732ccd875b3f/image.png" alt=""></p>
<p>그렇게 하나하나 점곱을 하다보면, 다음과 같은 28 x 28 x 1 크기의 output을 얻게 되는데 이를 <code>activation map</code> 이라고 한다. <code>activation map</code>은 이미지가 가지는 특징을 나타내어주는 역할을 한다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/367b9cd0-1e8f-45c4-8512-043772aead0b/image.png" alt=""></p>
<p>Convolution Neural Network (CNN) 에서는 filter를 하나만 사용하지는 않는다. 다양한 필터를 사용하여 필터마다 다른 특징을 나타내게 만든다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/8959efcb-f56a-4121-9398-18cc32fc14b9/image.png" alt=""></p>
<p>예를들어, 6개의 filter를 사용한다면 6개의 activation map이 나온다. 그렇게 나온 6개의 activation map을 모아 크기 28 x 28 x 6의 <em>새 이미지</em>를 얻을 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/0183f52f-fab9-4730-8b8e-b3ff51cb6151/image.jpg" alt=""></p>
<p>CNN에서는 입력 이미지가 convolution layer와 활성함수 ReLU를 통과하여 activation map을 얻고, 앞에서 얻은 activation map에 다시 convolution layer와 활성함수 ReLU를 통과하여 다시 activation map을 얻는 과정을 반복적으로 하게 된다. (ConvNet은 activation map이 흩어져 있는 Convolution Layers의 배열이다.)</p>
<p>그림을 설명하면 다음과 같다. 32x32x3의 입력 이미지를 6개의 5x5x3(입력 이미지의 depth가 3이므로 필터의 depth도 3)의 필터로 내적하여 28x28x6(필터의 개수가 6개이므로 depth가 6)의 activation map을 얻는다. 28x28x6의 activation map은 입력 이미지가 되어 10개의 5x5x6(입력 이미지의 depth가 6이므로 필터의 depth도 6)의 필터와 내적을 하고 24x24x10(필터의 개수가 10개이므로 depth가 10)의 activation map을 얻는다. 이 과정이 계속 반복된다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/10624b3a-1a05-4f47-9485-a12afaeccb9c/image.png" alt=""></p>
<p>필터가 여러개 쌓일수록 필터는 간단한 특징들(low-level)부터 가면 갈수록 더욱더 복잡하고, 정교한 특징들을 얻어내는 것이다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/94905232-b4e2-492c-872c-7227bcbaa7d9/image.png" alt=""></p>
<p>CNN의 기본 구조는 다음과 같이 이루어져 있다. Convolution layer에 활성함수인 ReLU를 쌓고, activation map의 크기를 줄여주는 pooling layer를 쌓는 방식을 여러 번 한 후, 마지막에 Fully connected layer를 쌓아 이미지를 클래스별로 분류한다.</p>
<h3 id="stride">Stride</h3>
<p><code>Stride</code> 는 보폭이라는 의미로 필터를 적용하는 간격을 정한다.
패딩을 크게하면 출력 데이터의 크기가 커지는 반면 스트라이드를 작게하면 출력 데이터의 크기는 작아진다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/a33c48bc-db79-4a05-b232-3efd8b135150/image.jpg" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/efa437a0-9dcb-4dcf-8326-b9da72fdea7c/image.jpg" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/31bfc84b-d8b0-4708-8d99-24bae2373837/image.jpg" alt=""></p>
<ol>
<li><code>Stride = 1</code>로 설정하고 slide하면서 점곱을 하면 ouput의 크기는 5 X 5가 된다.</li>
<li><code>Stride = 2</code>로 설정하고 slide하면서 점곱을 하면 ouput의 크기는 3 X 3이 된다.</li>
<li><code>Stride = 3</code>으로 설정하고 slide하면 7 X 7을 모두 커버할 수가 없다. 결국 마지막에 한줄이 남게 되는데, 그러면 오른쪽 한줄이 손실되는 결과를 가져오게 된다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/85da708c-36bb-4791-9ce7-3bc735f310e8/image.png" alt=""></p>
<p>산술적으로 생각해보면, $Output size =(N - F) / Stride + 1$ 이기 때문에 다음의 식을 적용하여 계산하면 <code>Stride = 3</code> 인 경우에 Output size가 자연수가 아닌 값이 나오게 된다. 하지만, 크기는 당연히 자연수여야하기 때문에 <code>Stride = 3</code> 인 경우는 성립하지 않는다.</p>
<h3 id="padding">Padding</h3>
<p><code>Padding</code> 이란 이미지의 가장자리 부분에 어떠한 숫자들을 채워 넣는 것을 의미한다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/8f15106f-4594-479f-831c-7270f6f972a8/image.png" alt=""></p>
<p>일반적인 경우엔 convolutional layer은 stride는 1로 하고, filter 크기가 F X F라면 padding은 (F-1)/2의 크기만큼 한다고 한다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/eb36cd98-7e3b-4e07-b4cb-a0c5ddffb8f8/image.png" alt=""></p>
<p><em>왜 Padding을 하는 것일까?</em>
$\rightarrow$ 이미지에 filter를 적용하면 깊이 말고 크기가 계속 줄어드는데, 이는 이미지의 부분적인 요소들을 보존하자는 취지를 생각하면 별로 좋지 못하기 때문에 입력데이터가 합성곱의 결과로 크기가 줄어들면서 데이터의 특성이 손실되는 것을 막고자, 입력데이터의 주변에 임의로 추가 픽셀을 둘러주는 것이다.</p>
<h3 id="summary">Summary</h3>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/6ac6965f-3639-4160-95d0-4f3331540afe/image.jpg" alt=""></p>
<hr>
<h2 id="pooling-layer">Pooling Layer</h2>
<p>Pooling layer는 특성을 잃지 않고 데이터 크기를 조정하는 역할을 한다.
<code>Pooling</code> 은 activation map의 크기를 downsampling 하는 과정을 말한다. 즉, 이미지의 크기를 줄이는 것이다. </p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/c1a9160f-b45f-4e3d-81c1-887bed09aef9/image.png" alt=""></p>
<p>여기서 <em>크기를 안줄이려고 padding 했는데 왜 크기를 줄이는거지?</em> 라는 의문점이 생긴다. </p>
<p><code>Padding</code> 을 한 것은 크기 보존뿐만 아니라, 이미지의 부분적 특징을 살리기 위해서 한 것이다. 계속 convolution을 하면 가장자리 부분의 특징이 잘 살아나지 못하기 때문이다.</p>
<p><code>Pooling</code>은 이미지의 특정 부분을 잘라내는 것이 아니라, 사진 전체 부분을 유지한 상태로 픽셀만 줄이는 것이다. </p>
<h3 id="max-pooling">Max pooling</h3>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/1226cd9d-8a47-4e03-8f87-aee49cfeff88/image.png" alt=""></p>
<p><code>Max Pooling</code> 은 크기별로 필터를 취해, 필터의 영역 안에서 가장 큰 값을 치해 downsampling하는 것이다. </p>
<p><em>여기서 주의해야 할 점은, 일반적으로 stride는 filter끼리 서로 겹치는 것은 지양해야 한다는 것이다. 가령, 위의 stride가 1이라면, 빨간 부분과 초록 부분 사이에 걸치는 부분이 생기게 되는데, 이러면 조금 곤란해진다.</em></p>
<p><em>더불어, Pooling Layer에는 zero-padding을 하는 것이 일반적이지 않다.</em></p>
<hr>
<h2 id="fully-connected-layer-fc-layer">Fully Connected Layer (FC Layer)</h2>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/8169af5f-b4b6-465a-9b4f-8f7742721992/image.png" alt=""></p>
<p>입력 이미지는 Convolution layer, ReLu, Pooling layer를 통과한 후, 마지막으로 FC layer를 통과한다. CNN의 마지막 층인 FC layer에서는 이전의 데이터를 1차원 벡터로 펴주고 가장 마지막에 Softmax에서 클래스를 구분하게 된다.</p>
<hr>
<h2 id="summary-1">SUMMARY</h2>
<p>CNN의 구성</p>
<ol>
<li>Convolutional Layer : 필터와 입력데이터의 합성곱을 이용해 입력데이터의 특성을 추출</li>
</ol>
<ul>
<li>Filter : depth는 입력데이터와 동일하게 유지. 입력데이터와 dot연산을 진행하여 모두 더한 값의 집합을 activation map으로 출력</li>
<li>Stride : Filter가 한번 이동하는 칸수</li>
<li>Padding : Convolution 시행시 데이터 크기를 유지하기 위해 입력데이터의 주변에 두르는 픽셀</li>
</ul>
<ol start="2">
<li>Pooling layer : 데이터의 크기를 줄이는 층</li>
</ol>
<ul>
<li>주로 Max pooling을 이용하고, Filter 안에서 가장 큰 입력데이터의 픽셀값을 뽑아 출력하게 됨.</li>
<li>입력데이터의 위치가 조금 변해도, 출력값이 유지됨</li>
</ul>
<ol start="3">
<li>Fully Connected Layer : 위의 결과를 1차원 벡터로 펴서 Softmax함수에 넣어 클래스를 구분</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS231N] Lecture 4 | Introduction to Neural Networks]]></title>
            <link>https://velog.io/@dorahee-ee/CS231N-Backpropagation-and-Neural-network</link>
            <guid>https://velog.io/@dorahee-ee/CS231N-Backpropagation-and-Neural-network</guid>
            <pubDate>Sun, 10 Mar 2024 18:59:27 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.youtube.com/watch?v=d14TUNcbn1k&amp;list=PL3FW7Lu3i5JvHM8ljYj-zLfQRF3EO8sYv&amp;index=4">CS231N - Lecture 4 | Introduction to Neural Networks</a></p>
<h2 id="backpropagation">Backpropagation</h2>
<p><strong>역전파 알고리즘은 출력값에 대한 입력값의 기울기(미분값)을 출력층 layer에서부터 계산하여 거꾸로 전파시키는 것이다.</strong></p>
<p>이렇게 거꾸로 전파시켜서 최종적으로 출력층에서의 output값에 대한 입력층에서의 input data의 기울기 값을 구할 수 있다.</p>
<p>이 과정에는 중요한 개념인 <code>chain rule</code>이 이용된다.</p>
<p>출력층 바로 전 layer에서부터 기울기(미분값)을 계산하고 이를 점점 거꾸로 전파시키면서 전 layer들에서의 기울기와 서로 곱하는 형식으로 나아가면 최종적으로 출력층의 output에 대한 입력층에서의 input의 기울기(미분값)을 구할 수가 있다. </p>
<p><code>Backpropagation</code>은 가중치를 계산하는 방식으로 생각하고, 
<code>Gradient Descent</code>는 가중치를 업데이트하는 방식이라고 생각하면 된다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/fefaaa52-4cac-409e-acd9-70463d26d94c/image.png" alt=""></p>
<p>우리가 알고자 하는 것은 <em>x가 f에 미치는 영향</em>, <em>y가 f에 미치는 영향</em>, <em>z가 f에 미치는 영향</em>이다.</p>
<ul>
<li>$\frac{df}{dx} =$ x가 f에 미치는 영향</li>
<li>$\frac{df}{dy} =$ y가 f에 미치는 영향</li>
<li>$\frac{df}{dz} =$ z가 f에 미치는 영향</li>
</ul>
<blockquote>
<p>덧셈 연산에서 미분은 1이고, 곱셈 연산에서는 서로의 값을 가지게 된다.
EX) $y = qz$ 이면, $\frac{dy}{dq} = z$ 이다.</p>
</blockquote>
<p>이제 최종 $f$값부터 반대로 거슬러 올라가며 가중치를 계산해주면 그림과 같은 값들을 얻게 된다.</p>
<p>앞서 말했던 영향력에 대해서 설명하면, $y$가 $h$만큼 증가하면 $f$는 $4h$만큼 감소한다. 이때 $q$는 *같은 그라디언트를 갖고 있기 떄문에 *똑같이 $h$만큼 증가한다. </p>
<h3 id="chain-rule-연쇄-법칙">Chain Rule (연쇄 법칙)</h3>
<p>: 합성 함수에 대한 미분 법칙</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/d102894c-0433-4b8e-8731-5c60d8fa2f0e/image.png" alt=""></p>
<p>$f$가 실함수(real-valued function)이고, $z=f(y)$이며 $y$는 $x$의 함수이고 $y=g(x)$라고 하면 $z$는 대입에 의해 $x$의 합수가 된다. 즉, $z=f(g(x))$이며, 다음과 같은 연쇄 법칙을 갖는다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/7ed0805f-ff7d-4450-8f6b-d7347559d391/image.png" alt=""></p>
<ul>
<li>Forward pass 시에 local gradient를 구한다.</li>
<li>Backward pass 시에 global gradient를 구한다.
$\rightarrow$ <code>local gradient * global gradient</code> 를 곱해서 gradient를 구하게 된다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/cbe2a4a4-6d64-4d5b-80c7-b3f60251e114/image.png" alt=""></p>
<p>시그모이드 함수가 예시로 나왔는데, 앞서 했듯이 아래의 식을 참조하여 그라디언트를 구하면 위의 그림과 같이 나온다. 하지만, 하나하나 chain rule을 이용하여 구한 그라디언트는 아래 그림과 같이 <code>sigmoid gate</code>로  한번에 계산이 가능한다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/90f1ea3d-2b7e-46a6-a996-e3821acd8736/image.png" alt=""></p>
<p><em>이것이 sigmoid 함수의 장점이다 !</em></p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/b2f4a465-f269-4eae-9c73-06288153c07e/image.png" alt=""></p>
<p>우리는 각 게이트를 일반화시킬 수 있다.</p>
<ul>
<li>add gate : gradient distributor (그라디언트를 동일한 값으로 전파해주는 역할)</li>
<li>max gate : gradient router (더 큰 값에 값을 부여해주는 역할)</li>
<li>mul gate : gradient switcher (서로의 값을 바꿔주는 역할)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS231N] Lecture 3 | Loss Functions and Optimization]]></title>
            <link>https://velog.io/@dorahee-ee/CS231N-Loss-Functionsand-Optimization</link>
            <guid>https://velog.io/@dorahee-ee/CS231N-Loss-Functionsand-Optimization</guid>
            <pubDate>Mon, 26 Feb 2024 12:39:41 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.youtube.com/watch?v=h7iBpEHGVNc&amp;list=PL3FW7Lu3i5JvHM8ljYj-zLfQRF3EO8sYv&amp;index=3">CS231N - Lecture 3 | Loss Functions and Optimization</a></p>
<h1 id="loss-function--optimization">Loss function &amp; Optimization</h1>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/b8247d49-04e5-4959-a90b-371f3b505aaf/image.png" alt=""></p>
<ol>
<li>Linear Classifier의 <em>F(X) = Wx + b</em> 에서 W의 값이 좋은 값인지 나쁜 값인지 <strong>정량화</strong>할 수 있는 것이 필요하고, 이것이 <code>Loss Function</code>이다.
$\rightarrow$ W가 얼마나 <em>안좋은지를</em> 판단한다. </li>
<li>Loss function의 값이 줄이면서 좋은 쪽으로 찾아가는 것을 <code>optimization</code>이라고 한다.</li>
</ol>
<h2 id="loss-function-손실-함수">Loss Function (손실 함수)</h2>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/d93c9d54-3788-4fdd-a013-4a72a1b251c6/image.png" alt=""></p>
<p><strong>Loss Function은 Classifier가 얼마나 좋은지 나타낸다 !</strong></p>
<ul>
<li>$x_i$ : 이미지</li>
<li>$y_i$ : label</li>
<li>$N$ : 전체 데이터 셋의 개수
$\rightarrow$ 전체 오차 값 $L$은 각 label별 오차를 더하고, 데이터의 개수로 나눈 값이 된다. </li>
</ul>
<p>그리고 이 값을 최소화하는 $W$를 찾는 것이 목표이다.</p>
<h3 id="multiclass-svm-loss">Multiclass SVM Loss</h3>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/1d97fa4a-7560-4c71-a7be-2e19b14ee81f/image.png" alt=""></p>
<p>SVM Loss는 Support Vector Machine Loss의 줄임말이다. SVM은 주로 분류 문제에 사용되며 서로 다른 클래스 간의 Margin을 두어 두 클래스가 일정 거리이상에 위치하도록 한다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/79b3d5e6-fb42-48a0-9e50-6be5e13ca1cb/image.png" alt=""></p>
<p>SVM Loss는 굉장히 간단한 손실 함수 중 하나이다.</p>
<ul>
<li>$s_j$ : 정답이 <strong>아닌</strong> 클래스</li>
<li>$s_{yi}$ : 정답 클래스</li>
</ul>
<ol>
<li>정답 스코어가 정답이 아닌 스코어 + Safety Margin 보다 높으면 Loss는 0이 된다.</li>
<li>그렇지 않으면 그 차이의 최댓값을 Loss로 사용한다.</li>
</ol>
<h4 id="q1--what-happens-to-loss-if-car-scores-change-a-bit">Q1 : What happens to loss if car scores change a bit?</h4>
<p>$\rightarrow$ car iamge를 조금 바꾼다고 해서 loss가 바뀌지 않는다. SVM Loss는 오직 <strong>정답 클래스가 다른 클래스에 비해 높은지</strong>에만 관심이 있기 때문이다.</p>
<h4 id="q2--what-is-minmax-possible-loss">Q2 : What is min/max possible loss?</h4>
<p>$\rightarrow$ 위에 있는 hinge loss 그래프를 보면 알 수 있듯이 최소는 0이고, 최대는 무한대이다.</p>
<h4 id="q3--at-initialization-w-is-small-so-all-s-approx-0-what-is-loss">Q3 : At initialization $W$ is small so all $s \approx 0$. What is loss?</h4>
<p>$\rightarrow$ <code>클래스 수 - 1</code> 의 값이 나온다. 이 방법은 디버그용으로 많이 사용한다. 즉, 0으로 해주면 loss가 잘 나오는지를 검사해보는 것이다. 이걸 sanity check라고도 부른다.</p>
<h4 id="q4--what-if-the-sum-was-over-all-classes-including-j--y_i">Q4 : What if the sum was over all classes? (including $j = y_i$)</h4>
<p>$\rightarrow$ 최종 loss값이 1이 증가된다. 우리의 목표는 loss값이 0이 되도록 하는 것인데 이렇게 되면 1이 가장 좋은 값이 된다. 그렇기 때문에 정답 클래스를 빼서 loss가 0이 되도록 하는것이다.</p>
<h4 id="q5--what-if-we-used-mean-instead-of-sum">Q5 : What if we used mean instead of sum?</h4>
<p>$\rightarrow$ Sum 대신 Mean을 사용해도 별 차이는 없다. 단지 scale만 작아진다. 우리는 loss가 크고 작고에 관심이 있는 것이기 때문에 scale이 커지든 작아지든 상관없다.</p>
<h4 id="q6--what-if-we-used-displaystyle-l_i--sum_jne-y_i-max0-s_j-s_y_i12">Q6 : What if we used $\displaystyle L_i = \sum_{j\ne y_i} max(0, s_j-S_{y_i}+1)^2$</h4>
<p>$\rightarrow$ 제곱을 하게 되면 다른 결과가 나온다. 우선 제곱을 하게 되면 <strong>non-linear</strong> 해진다. 그렇게 되면 hinge loss 그래프에서 직선이 아니라 곡선으로 올라가게 되고, &#39;매우매우 좋다&#39; 또는 &#39;매우매우 나쁘다&#39;를 표현할때 유용할 수 있다. 
그리고 저렇게 제곱을 해서 사용하는 <code>square hinge loss</code>라는 방법이 따로 있다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/9e5b05a4-d8fe-4925-9e2e-20f0c2206eb9/image.png" alt=""></p>
<h2 id="softmax-classifier-multinomial-logistic-regression">Softmax Classifier (Multinomial Logistic Regression)</h2>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/d95328a0-4f67-431e-88de-5aba1cc11703/image.png" alt=""></p>
<p>Softmax function은 모든 스코어들을 exp 취하고, 다 더한 뒤에 원하는 클래스의 점수를 exp 취해서 나눈다. 그리고 내가 원하는 정답 클래스에 $-log$를 취해서 loss를 구한다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/fbd88237-7b14-4e1e-b904-444692168a4c/image.png" alt=""></p>
<ol>
<li>각 클래스마다 점수를 구한다.</li>
<li>각 점수로 $e$를 제곱해준다.</li>
<li>정규화를 해서 확률로 만든다. (확률이기 때문에 모두 더하면 1)</li>
<li>$-log$를 취한다.
$\rightarrow$ 작은 값은 큰 값으로 나오고, 큰 값은 작은 값으로 나오니깐, 실제 오차값을 구할 수 있다.</li>
</ol>
<p><strong>각 점수마다의 확률을 구하여 오차값을 구하는 방식을 Softmax라고 부른다.</strong></p>
<h3 id="-log를-취하는-이유">-log를 취하는 이유</h3>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/841475b6-70c4-49a0-bdd8-419f61172815/image.png" alt=""></p>
<p>x축이 확률, y축이 loss라고 생각하면 $-log$를 보면 확률이 1에 가까워질수록(정답률이 높아질수록) loss가 0에 가까워진다. 그래서 $-log$를 사용한다.</p>
<h2 id="svm-vs-softmax">SVM vs Softmax</h2>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/cb83d718-2c6b-4e26-9a41-d1de324e741c/image.png" alt=""></p>
<h3 id="svm">SVM</h3>
<p>Loss가 0이 되는 순간, 자기 할 일 다 한 것이다. max값만을 구하기 때문에, 정답 카테고리가 y [0]이고, 점수가 [10,9,9,9]같이 나오더라도 Loss값은 0이 된다. 그리고 이걸 완벽한 Loss로 본다.
또한, datapoint를 약간 흔들어주는 경우에도 <em>정답 클래스가 정답이 아닌 클래스에 비해 높은지</em>에 신경쓰기 때문에 딱히 영향이 없다. 즉, <strong>둔감하다</strong>.</p>
<h3 id="softmax">Softmax</h3>
<p>최대한 정답 카테고리의 확률을 높이기를 원하기 때문에, 계속해서 확률을 높이려고 노력한다. 쭉쭉 올라가서 점수가 [1000,5,3,1] 같아져도 계속해서 확률을 높이기 위한 방향으로 나아간다. 0에 굉장히 근접한 값은 나올 수 있지만 웬만해선 loss가 0이 나오지 않는다.
또한, datapoint를 약간 흔들어주는 경우를 보면 softmax는 확률로 계산되기 때문에 조금만 데이터가 변형되어도 바로 확률에 영향을 미친다. 즉, <strong>민감하다</strong>.</p>
<hr>
<h2 id="regularization">Regularization</h2>
<p>: 복잡해진 classifier에 페널티를 가해서 최대한 간결하고 심플하게 만들어주는 방법이다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/8a606ff2-bd74-46d1-b52a-5bd35c9411f4/image.png" alt=""></p>
<p>우리가 $W$값을 잘 조정해서 loss값을 0으로 만들었다고 이게 완벽한 classifier인 것은 아니다. 우리가 loss 값이라고 만들어 놓은 것은 결국 <strong>train set에 대한 loss값</strong>이기 때문이다. 
<strong>우리가 실질적으로 궁금한 것은 test set에 대한 loss값이다!</strong></p>
<p>파란점이 train data, 초록점이 test data이다. 여기서 우리가 train data에 대해서 loss값을 0으로 만들면 파란선과 같은 classifier가 작동할 것이다. 그리고 이 classifier로 test data를 예측한다면 엉망인 결과를 도출할 것이다. 이것을 <code>Overfitting(과적합)</code>이라고 한다. train data에 맞춰진 나머지 test data에 대해서 성능이 좋지 못하는 문제를 말하는 것이다. 
그리고 초록선과 같은 classifier가 우리가 지향하는 classifier가 될 것이다. train set에도 어느정도 잘 맞으면서 test set에도 어느정도 잘 맞추는 선이기 때문이다. </p>
<p><strong>우리는 classifier가 최대한 간결하고 심플하게 되는 것을 선호한다!</strong></p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/11bc1053-e898-489e-b8bd-dcaca13000ad/image.png" alt=""></p>
<p>$R(W)$가 regularization의 식이고, $R(W)$가 너무 강해져버리면 너무 간결함만을 유지하고자해서 loss값을 맞춰주는게 큰 의미가 없어지고, 너무 작으면 regularization을 하는 의미가 없어지기 때문에 $\lambda$를 하이퍼파라미터로 사용하여 $R(W)$의 값을 적당히 맞춰주는 역할로 활용할 것이다.</p>
<h3 id="l2-regularization-weight-decay">L2 Regularization (Weight Decay)</h3>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/e4f95835-7c8e-411e-ba79-b9a54e0497f6/image.png" alt=""></p>
<p>$R(W)$가 L2 Regularization이고, $x$가 이미지 픽셀값이라고 했을 때,
$w_1$과 $w_2$를 이용해 dot product를 구하면 1이라는 동일한 값이 나온다.</p>
<p><em>이때 L2 Regularization은 어떤 $w$값을 더 간결하다고 생각할까?</em></p>
<p><strong>$R(W)$는 값들의 제곱을 더했기때문에 $w_2$가 더 좋다고 생각할 것이다.</strong>
즉, L2는 $w$값이 최대한 평평하게, 너무 큰 값 없이 최대한 비슷한 값으로만 이루어지게 하고 싶은 것이다. 이러면, 이미지에 가는 가중치들이 다들 비슷비슷해 지므로, 조금 더 전체적인 그림을 볼 수 있지 않을까? 하는 느낌인 것이다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/f5a03b5b-6776-4e33-94e6-c2564ae8c202/image.png" alt=""></p>
<p>(x, y)의 데이터셋을 가지고 있을 때, 점수를 dot product를 통해서 구한 후, SVM 또는 Softmax와 같은 loss function으로 얼마나 오차가 있는지를 알아낼 것이다.</p>
<p>또한, Regularization , 즉 $R(W)$를 함께 더해서 최종 loss값을 구할 수 있다.</p>
<hr>
<h2 id="optimization">Optimization</h2>
<h3 id="strategy-1-random-search">Strategy #1 Random Search</h3>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/7a12a258-7cb7-4ed8-8c11-4a10b69fb5e3/image.png" alt=""></p>
<p>Random Search는 <em>가장 멍청한 방법</em>이다. <code>Random Search</code>라는 단어 그대로 $W$의 값을 랜덤하게 바꿔가면서 최적의 값을 찾아나가는 방법이다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/a6b121e0-387d-4659-a176-5d59912903d2/image.png" alt=""></p>
<p><code>Random Search</code>를 test set에다가 적용시키면 15.5 % 정도의 정확도가 나온다. SOTA(State Of The Art : 현대 기술)로는 95 % 정도까지 나오는 것을 생각하면 아주 좋지 않은 결과라고 볼 수 있다.</p>
<h3 id="strategy-2-follow-the-slope-gradient-descent">Strategy #2 Follow the slope (Gradient Descent)</h3>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/21d7c48e-4f89-493a-993a-8b850d55db55/image.png" alt=""></p>
<p>$W$를 찾는 과정이 산골짜기에서 산을 내려오는 것이라고 생각했을 때, 땅 주변에 발을 가져다 대 보면서 좀 낮은 곳으로 조금 움직인 다음 다시 한번 발을 갖다 대 보며 더욱더욱 낮은 곳으로 내려가보는 방법이다. 그리고 이 방법은 <code>경사하강법</code>이다.</p>
<p>여기서 <strong>미분</strong>이라는 개념이 등장한다. 우선 간단하게 &#39;미분은 어떤 함수의 기울기를 구한다&#39;라고 생각하자. 대충 미분을 쓸 거고, 위의 식 $\displaystyle\lim_{h\rightarrow0}\frac{f(x+h)-f(x)}h$ 을 써볼 거다라는 것만 알아두면 된다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/6a84e833-70f6-495c-b33e-03879c96ac15/image.png" alt=""></p>
<ul>
<li>현재 $W$값으로는 loss가 1.25347이 나온다고 가정한다.</li>
<li>아주 작은 0.0001만큼 움직였더니 loss가 줄어들었다.</li>
<li>위에 있는 값으로 미분을 하면 기울기가 -2.5가 나온다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/aee3224e-2e41-4eb5-b3d5-252dbdcbfb97/image.png" alt=""></p>
<ul>
<li>다시 0.0001만큼 움직였더니 이번에는 loss가 늘어났다.</li>
<li>이 값들로 미분을 하면 기울기가 0.6이 나온다.</li>
</ul>
<p>이렇게 계속 0.0001씩 더해서 미분하는 것은 굉장한 시간 낭비가 될 것이다. </p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/91055be9-65a6-4c5b-a896-422765c0df19/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/dd0f2269-6487-4133-97da-d9b175b9c3e6/image.png" alt=""></p>
<p>그렇기 때문에 뉴턴과 라이프니치의 방법으로 수학적으로 미분을 간단하게 할 수 있고, 미분값들이 구해진 것을 볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/af03dcec-56ef-43c3-adae-1fbfc0ed048f/image.png" alt=""></p>
<p>$\displaystyle\lim_{h\rightarrow0}\frac{f(x+h)-f(x)}h$ 한 것이 <code>numerical</code>, 그냥 미분 공식으로 나온 것이 <code>analytic</code>이다.</p>
<ul>
<li><p><code>numerical</code>로 하면, 느리고, 정확하진 않지만(컴퓨터가 소수점을 약간은 빼버리니깐..) 코드 작성은 쉽다.</p>
</li>
<li><p><code>analytic</code>으로 하면 빠르고, 정확하지만 코드 작성이 어렵다.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/20d15a55-62a2-4c18-8c32-f08d5a4d8f34/image.png" alt=""></p>
<p>Gradient Descent, 즉 기울기 하강을 통하여 가장 적절한 W값을 찾아낼 것이다. <code>evaluate_gradient</code>에서 미분 값을 계산하고, <code>weights</code>를 <code>step_size * 기울기</code>만큼 빼면서 최적의 값을 찾는다.</p>
<p>여기서 <code>step size</code>란 <code>learning rate</code>라고도 불리며 앞선 예시에서 0.0001과 같은 하이퍼파라미터 값을 말한다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/c9bff5f2-5cc6-48d0-b0cb-59e20921d44e/image.png" alt=""></p>
<p>위의 그래프가 3차원 그릇같은 모양이라고 했을 때, 우리는 가장 움푹 파인 부분인 가운데로 가야한다. 이때 <code>step size</code>가 너무 크게되면 가운데를 지나쳐 보라색까지 갈 것이고, 너무 작으면 가운데까지 도달하는데 엄청 오랜 시간을 학습해야 할 것이다. </p>
<p>그렇기 때문에 <code>step size</code>를 최대한 적절하게 조정해서 원하는 곳까지 도달하게 해야한다.</p>
<h3 id="stochastic-gradient-descent-sgd">Stochastic Gradient Descent (SGD)</h3>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/6485e5be-6890-438b-b846-07ba0a14c5f7/image.png" alt=""></p>
<p>모든 사진들을 앞서 설명한 방법대로 계산하기에는 엄청난 시간이 걸릴것이다. Imagenet Challenge에서는, 대략 1.3 Million, 즉 130만 개의 training set이 있는데, 이 사진 하나하나를 죄다 미분하고 계산하고 하기엔 너무 느리고, 오래 걸린다. </p>
<p>SGD는 iteration마다 <code>mini batch</code>라는 랜덤한 샘플들을 뽑아서 그 샘플들에 대한 loss를 계산하고, weight를 계산하는 방법이다. 이렇게 되면 전체 $N$개의 대해서 gradient descent를 구하는 것보다 훨씬 시간을 절약할 수 있게 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS231N] Lecture 2 | Image Classification]]></title>
            <link>https://velog.io/@dorahee-ee/CS231N-Image-Classification</link>
            <guid>https://velog.io/@dorahee-ee/CS231N-Image-Classification</guid>
            <pubDate>Sat, 27 Jan 2024 12:27:33 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.youtube.com/watch?v=OoUX-nOEjG0&amp;list=PL3FW7Lu3i5JvHM8ljYj-zLfQRF3EO8sYv&amp;index=2">CS231N - Lecture 2 | Image Classification</a></p>
<h1 id="image-classification">Image Classification</h1>
<h4 id="-a-core-task-in-computer-vision">= A core task in Computer Vision</h4>
<p><code>input image</code> 를 입력받아서 <code>given set of discrete lables</code> 에서 맞는 것에 <code>assign</code> 해준다.</p>
<h2 id="semantic-gap">Semantic Gap</h2>
<p>Semantic Gap(의미적 차이) : 사람과 컴퓨터의 인식의 차이
<img src="https://velog.velcdn.com/images/dorahee-ee/post/3ffd73b1-c994-4783-83d7-f08ffb41ed45/image.png" alt=""></p>
<ul>
<li>사람은 직관적으로 사물이 무엇인지 알아낼 수 있지만 컴퓨터는 직관이 없다(그저 숫자일뿐이다).</li>
<li>컴퓨터는 픽셀의 색을 표현한 숫자로 된 그리드(격자)를 통해 인식한다.<ul>
<li>각 픽셀당 0-255 사이의 값을 갖는다.</li>
</ul>
</li>
</ul>
<h3 id="challenges">Challenges</h3>
<h4 id="1-viewpoint-variation-시점의-변화">1. Viewpoint variation (시점의 변화)</h4>
<p>: 카메라가 움직이면 모든 픽셀들의 값이 바뀐다.
<img src="https://velog.velcdn.com/images/dorahee-ee/post/32df20d9-5266-4f6a-86de-d7135e3e0da2/image.png" alt=""></p>
<h4 id="2-illumination-조명">2. Illumination (조명)</h4>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/db1223a6-b149-4d0e-bbaf-99195f9bd070/image.png" alt=""></p>
<h4 id="3-deformation-변형">3. Deformation (변형)</h4>
<p>: 다양한 자세나 형태에 따라 이미지의 분류가 어려울 수 있다.
<img src="https://velog.velcdn.com/images/dorahee-ee/post/c758e459-ee0c-477b-ab7d-423ca0614a22/image.png" alt=""></p>
<h4 id="4-occlusion-가려짐">4. Occlusion (가려짐)</h4>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/8c2baf24-de50-4fc1-9f25-5656f1c9d077/image.png" alt=""></p>
<h4 id="5-background-clutter-배경과-사물이-비슷함">5. Background clutter (배경과 사물이 비슷함)</h4>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/38a785c1-9af5-4707-a459-05c39ae77dbe/image.png" alt=""></p>
<h4 id="6-intraclass-variation-다양성">6. Intraclass variation (다양성)</h4>
<p>: 다양한 종류의 Object들을 하나의 label로 분류하기 어려울 수 있다.
ex) 다양한 종의 고양이들
<img src="https://velog.velcdn.com/images/dorahee-ee/post/27a5ca32-621c-4f42-b663-f1298a2df893/image.png" alt=""></p>
<p><strong>Algortithms should be robust to these different kinds of transformations !!</strong></p>
<hr>
<h2 id="image-classifier">Image Classifier</h2>
<p><strong>no obvious way</strong> to hard-code the algorithm for recognizing a cat, or other classes.</p>
<pre><code class="language-python">def classify_image(image):
 # some magic here?
    return class_label</code></pre>
<p>결국 다음 함수와 같이 <code>input image</code>를 받아서 <code>class label</code>을 반환해주는 함수가 우리가 원하는 함수가 될 것이다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/661d0246-0a1c-48fd-8cb9-b549141f6ce8/image.png" alt=""></p>
<p>이미지에서 <code>edges</code>를 추출하고 귀모양, 코모양과 같이 고양이에게 필요한 집합들을 하나하나 찾아서 다 있으면 고양이라는 <code>label</code>을 출력하는 방법이 있을 것이다. 하지만, 이와 같은 방법은 변화에 강건하지 않아서 결과값이 잘 나오지 않고, 강아지와 집과 같은 다른 객체들을 인식하려고 할 때 그 클래스에 맞는 집합을 따로 하나하나 만들어야돼서 굉장히 비효율적이다.</p>
<h3 id="data-driven-approach-데이터-중심-접근법">Data-Driven Approach (데이터 중심 접근법)</h3>
<p>위와 같이 고양이, 강아지 등 특정 객체에 필요한 규칙들을 하나하나 만드는 것이 아니라 엄청나게 방대한 고양이, 강아지, 집 사진들을 컴퓨터에게 제시하고, <code>machine learning classifier</code>을 학습시키는 방법이다. </p>
<ol>
<li>Collect a dataset of images and labels</li>
<li>Use Machine learning to train a classifier</li>
<li>Evaluate the classifier on new images</li>
</ol>
<h3 id="nearest-neighbor-classifier">Nearest Neighbor Classifier</h3>
<h4 id="train--data와-label을-기억한다">train : data와 label을 기억한다.</h4>
<pre><code class="language-python">def train(images, labels):  # Memorize all data and labels
 # Machine Learning!
    return model</code></pre>
<h4 id="predict--새로운-image를-training-image와-비슷한-것을-찾아서-labeling한다">predict : 새로운 image를 training image와 비슷한 것을 찾아서 labeling한다.</h4>
<pre><code class="language-python">def predict(model, test_images):  # Predict the label if the most similar training image
 # Use model to predict labels
    return test_labels</code></pre>
<p><code>image</code>와 <code>label</code>을 입력 받아 머신러닝을 시키는 <code>train</code> 함수와 <code>train</code> 함수에서 반환된 모델을 가지고 테스트 이미지를 판단하는 <code>predict</code> 함수로 나누어진다.</p>
<h3 id="example-dataset--cifar10">Example Dataset : CIFAR10</h3>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/7301ec40-be59-4d91-9cdc-48588a630c09/image.png" alt=""></p>
<h3 id="l1-distance-manhattan-distance">L1 Distance (Manhattan distance)</h3>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/8acd10aa-d050-4571-80c1-a91e68ce7a14/image.png" alt="">
test image와 train image는 각각 같은 사이즈를 갖고, 같은위치의 픽셀값 차이를 구해 단순히 합하게 된다. 이 값이 가장 적은 사진이 비슷한 사진으로 골라지는 것이다. <strong>하지만, 단순히 픽셀값을 비교하는 것으로는 붉은벽돌이랑 붉은지갑이랑 같은 물체로 구분할 수 있는 가능성이 있다.</strong></p>
<h4 id="python-code">python code</h4>
<pre><code class="language-python">import numpy as np

class NearestNeighbor:
    def __init__(self):
        pass

    # Memorize training data (just remembers all the training data)
    def training(self, X, y):
        self.xtr = X     # X : matrix (N x D)
        self.ytr = y    # y : matrix (1 x N)

    def predict(self, X):
        num_test = X.shape[0]
        Ypred = np.zeros(num_test, dtype = self.ytr.dtype)

        for i in xrange(num_test):
            # using the L1 distance
            # -&gt; for each test image, find closet train image
            distances = np.sum(np.abs(self.xtr - X[i,:]), axis = 1)
            min_index = np.argmin(distances)
            Ypred[i] = self.ytr[min_index]

        return Ypred </code></pre>
<ul>
<li>수행시간 : Train O(1), Predict O(N)</li>
<li>보통의 경우 Training이 오래걸리고, Test는 빠르다. <strong>하지만 Nearest Neighbor는 반대이다.</strong></li>
</ul>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/561fcd6d-5b0f-40aa-a596-398203ed956b/image.png" alt=""></p>
<ul>
<li>점은 학습데이터이고, 점의 색깔은 class label이다.</li>
<li>각 좌표가 어떤 학습 데이터와 가장 가까운지를 계산한 후에 그 학습데이터의 label을 반환한다.</li>
<li>NN 분류기는 space에 따라 데이터를 분류한다.</li>
</ul>
<p><strong>문제점</strong></p>
<ul>
<li>초록색 label사이에 노란색 label(noise)이 있다.</li>
<li>깔끔하게 나누어져있지 않다.</li>
</ul>
<h3 id="k-nearest-neighbors">K-Nearest Neighbors</h3>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/8a300aaa-fd2e-4ef7-819f-0d8c1831b5db/image.png" alt=""></p>
<ul>
<li>K-NN방식은 test data와 train data 사이의 거리 (NN방식에서의 픽셀차이값과 같은것) 가 가장 짧은 K개의 train data들을 고르고, 그 중 가장 다수의 class로 결정되는 모델이다. </li>
<li>K가 커질수록 위의 그림처럼 분류할 때 혼자 튀는 데이터들은 무시해서 train data에서 소수의 오류를 감안하고, test data에서의 성능을 높이게 된다.</li>
</ul>
<h4 id="distance-metric">Distance metric</h4>
<p>데이터간의 거리(distance)란 데이터간의 유사성을 말한다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/431019d1-d636-4f0a-85c1-ef9765e4694b/image.png" alt=""></p>
<ol>
<li><p>L1 Distance
다차원에서 단순히 두 점의 좌표들간의 차이의 절댓값을 더한 것으로 어떤 좌표계를 사용하느냐에 따라 값이 바뀐다.</p>
</li>
<li><p>L2 Distance
원점을 기준으로 모든 거리가 같기 때문에 좌표계에 따라 값이 바뀌지 않는다.</p>
</li>
</ol>
<blockquote>
<p><strong>Q . Where L1 distance might be preferable to using L2 Distance ?</strong>
<strong>A . Input feature가 개별적인 의미를 가지고 있다면 L1이 좋지만, 일반적인 벡터이고 실질적인 의미를 가지고 있지 않다면 L2가 좋다.</strong></p>
</blockquote>
<p><a href="https://bookandmed.tistory.com/27">룰루랄라 효니루 - [CS231n] 2강. L1 &amp; L2 distance</a></p>
<h3 id="hyperparameters">Hyperparameters</h3>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/90ba5867-5615-41f3-9bc8-b48f776793f1/image.png" alt=""></p>
<ol>
<li><p>train data에서 젤 잘 작동하는 hyperparameter로 결정하는 방식 (K=1이고 검증시에도 학습데이터를 사용)</p>
<p>:  K=1의 경우 학습데이터자체는 완벽히 분류하게 되지만, 새로운 데이터로 test해보면 튀는 데이터로 인한 복잡한 분류로 인해 오히려 성능이 떨어지게 된다. </p>
</li>
<li><p>train &amp; test data로 나누고, test data에서 최적인 hyperparameter로 결정하는 방식</p>
<p>: 이 역시 새로운 데이터를 만나면 성능이 떨어질 수 있다.</p>
</li>
<li><p>가진 데이터를 train, val, test 세개로 나누는 경우</p>
<p>: train data로 알고리즘을 학습하고 validation set으로 최적 hyperparameter를 찾고, test data를 마지막으로 실제 성능을 unseen data에서 확인해본다.</p>
</li>
<li><p>Cross-validation (교차검증)</p>
<p>: validation set를 따로 설정하는 것이 의미가 없을정도로 데이터가 부족한 경우, train set를 K개의 fold로 나누어 돌아가면서 validation set 역할을 한다. 이를 K-교차검증이라고 한다.
<strong>Dateset이 작다면 사용하지만, 딥러닝은 데이터의 양 자체가 방대하기 때문에 교차검증을 사용하지 않는다.</strong></p>
</li>
</ol>
<h3 id="k-nn-알고리즘을-이미지-분류에-사용하지-않는-이유">K-NN 알고리즘을 이미지 분류에 사용하지 않는 이유</h3>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/551d9450-6a05-4963-bce2-8e9a11bd771e/image.png" alt=""></p>
<ol>
<li>시간이 오래 걸린다.</li>
<li>L1/L2 distance가 이미지간의 거리를 구하는 것에 적합하지 않다.</li>
<li>Curse of dimensionality<ul>
<li>k-nn은 training data를 이용해서 공간을 분할하기 때문에 k-nn이 잘 동작하려면 전체 공간을 조밀하게 커버할만큼의 데이터가 필요하다.</li>
<li>즉, 충분한 양의 학습데이터가 필요한데 이 학습데이터는 차원이 증가함에따라 기하급수적으로 증가한다. 1차원에서는 4개의 데이터만 있으면 되지만, 3차원에서는 많은 데이터가 필요하게 된다.</li>
</ul>
</li>
</ol>
<hr>
<h2 id="linear-classification">Linear Classification</h2>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/1496b634-eb05-409e-aa8a-43f968c3ba38/image.png" alt=""></p>
<ul>
<li>Neural Network(NN)는 레고 블럭과 같고, Linear classifier은 기본 블럭과 같다.</li>
<li>그림과 같이 여러 층을 쌓고, 각 층은 Linear classifier에 의해서 분류과정이 일어나게 된다.</li>
</ul>
<h3 id="parametic-approach">Parametic Approach</h3>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/28e29230-0502-4538-90af-2cd3b71ab8cd/image.png" alt=""></p>
<p>총 개수 <code>가로픽셀수 x 세로픽셀수 x 채널수(컬러면 RGB로 3, 흑백은 1)</code>로 이루어진 데이터들을 행렬계산을 위해 벡터로 쫙 펼치고, 각 클래스를 대변할 수 있는 parameter인 <code>W(가중치, weights)</code>와 <code>b</code>를 설정하여 결괏값을 구해 가장 score가 높은 class로 분류하게 된다. 여기서 사용되는 함수 f(x, W)가 직선의 방정식이라서 <code>linear classifier</code>라고 부른다.</p>
<ul>
<li>Score : 이미지의 픽셀값과 가중치 행렬을 내적한 값에 bias term을 더한 것</li>
<li>내적 : 클래스간 템플릿의 유사도를 측정하는 것과 유사</li>
<li>bias : 데이터 독립적으로 각 클래스에 scailing offset을 더함</li>
</ul>
<h3 id="linear-classifier로-분류하기-어려운-경우">Linear Classifier로 분류하기 어려운 경우</h3>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/cd9f6c76-6fa0-4acc-9898-cc5d6ec1aa42/image.png" alt=""></p>
<p>2, 3번째 그림과 같이 직선만으로는 나뉠 수 없는 경우가 있고, 이를 해결하기 위해 여러층의 신경망을 쌓게 되는 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[코드트리] 완전탐색, 시뮬레이션]]></title>
            <link>https://velog.io/@dorahee-ee/%EC%BD%94%EB%93%9C%ED%8A%B8%EB%A6%AC-%EC%99%84%EC%A0%84%ED%83%90%EC%83%89-%EC%8B%9C%EB%AE%AC%EB%A0%88%EC%9D%B4%EC%85%98</link>
            <guid>https://velog.io/@dorahee-ee/%EC%BD%94%EB%93%9C%ED%8A%B8%EB%A6%AC-%EC%99%84%EC%A0%84%ED%83%90%EC%83%89-%EC%8B%9C%EB%AE%AC%EB%A0%88%EC%9D%B4%EC%85%98</guid>
            <pubDate>Fri, 11 Aug 2023 10:49:03 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>학교에서 진행하는 여름방학 코딩테스트 대비 캠프 <em>with</em> <a href="https://www.codetree.ai/">CodeTree</a> 의 강의와 문제풀이를 정리한 내용입니다.</p>
</blockquote>
<h1 id="완전탐색이란">완전탐색이란?</h1>
<p>완전탐색이란, <strong>문제를 해결할 수 있는 가장 naive한 방법</strong>이다. 이는 모든 가능한 경우를 다 따져보는 것으로, 코드를 작성하기가 쉽다는 장점이 있지만 모든 가능한 경우를 전부 계산해봐야 하므로 올바른 정답을 찾기까지 걸리는 시간(프로그래 수행시간)이 비교적 오래 걸린다는 단점이 있다.
<em>따라서 완전탐색은 시간복잡도를 계산해보고, 시간초과가 나지 않을 것 같다면 항상 적용해야 하는 방법이라고 할 수 있다.</em></p>
<h2 id="격자-안에서-완전탐색">격자 안에서 완전탐색</h2>
<h3 id="✏️-연습문제--최고의-33위치">✏️ 연습문제 : 최고의 33위치</h3>
<p><code>N * N 크기의 격자 정보가 주어집니다. 이때 해당 위치에 동전이 있다면 1, 없다면 0이 주어집니다. N * N 격자를 벗어나지 않도록 3 * 3 크기의 격자를 적절하게 잘 잡아서 해당 범위 안에 들어있는 동전의 개수를 최대로 하는 프로그램을 작성해보세요.</code></p>
<p>이 문제의 경우 어느 지점을 잡아야 최대의 돈을 얻을 수 있을지 바로 판단하기가 어렵다.
<em>따라서 가능한 모든 지점에 3x3 크기의 사각형을 놓아보며, 얻을 수 있는 돈을 세고, 그 중 최댓값을 구하면 된다.</em></p>
<ol>
<li>모든 가능한 3x3 격자의 좌측 상단 모서리를 잡아본다.</li>
</ol>
<p><em>단, 이때 격자를 벗어나는 경우는 제외한다.</em>
2. 좌측 상단 모서리가 잡혔다면, 3x3 칸 내 동전의 수를 세어 최댓값을 갱신한다.</p>
<h4 id="💻-code">💻 CODE</h4>
<pre><code class="language-python">n = int(input())
grid = [list(map(int, input().split())) for _ in range(n)]

# (row_s, col_s) ~ (row_e, col_e) 사이의 금의 개수를 계산하는 함수
def get_num_of_gold(row_s, col_s, row_e, col_e):
    num_of_gold = 0

    for row in range(row_s, row_e + 1):
        for col in range(col_s, col_e + 1):
            num_of_gold += grid[row][col]

    return num_of_gold

max_gold = 0

# 완전탐색
for row in range(n):
    for col in range(n):
        # 3 * 3 격자가 n * n 격자를 벗어나는 경우는 계산 X
        if row + 2 &gt;= n or col + 2 &gt;= n:
            continue

        num_of_gold = get_num_of_gold(row, col, row + 2, col + 2)

        # 최댓값 갱신
        max_gold = max(max_gold, num_of_gold)

print(max_gold)</code></pre>
<hr>
<h1 id="시뮬레이션이란">시뮬레이션이란?</h1>
<p>시뮬레이션이란 문제에서 제시한 알고리즘을 한 단계식 차례대로 수행하여 해결하는 방법을 말한다. 이런 문제의 경우 문제를 해결하는데 있어 아이디어를 떠올리기는 쉽지만 떠올린 아이디어를 실제로 코드로 옮기는 과정이 어렵다.</p>
<h2 id="격자-안에서-밀고-당기기">격자 안에서 밀고 당기기</h2>
<p>밀고 당기는 작업이란, 주어진 숫자들을 특정 방향으로 1칸씩 미는 작업을 뜻한다. 2차원 격자에서는 선택된 행에 대해, 적혀있는 숫자들을 왼쪽 혹은 오른쪽으로 한 칸씩 밀어주는 작업이 될 수 있고, 혹은 선택된 열에 대해 적혀있는 숫자들을 위 혹은 아래 방향으로 한 칸씩 밀어주는 작업이 될 수도 있다.</p>
<center><img src="https://velog.velcdn.com/images/dorahee-ee/post/8dd9acb0-cbe5-4e0b-bba8-4dcf2b4c3064/image.png" width="50%" height="50%"></center>

<ol>
<li>가장 끝에 있는 원소가 유실되는 것을 방지하기 위해서 가장 끝의 원소를 <code>temp</code>라는 변수를 저장한다.</li>
<li>가장 끝 칸(n-1)부터 앞으로 오며 순서대로 채운다.</li>
</ol>
<p><em>앞에서부터 당기는걸 반복한다면, 정보가 중간에 유실될 수 있다.</em>
3. 모든 원소를 채웠다면, 가장 앞 칸에 temp에 저장되어 있던 값을 넣어준다.</p>
<h3 id="✏️-연습문제--컨베이어-벨트">✏️ 연습문제 : 컨베이어 벨트</h3>
<p><code>시계 방향으로 한 칸씩 회전하는 컨베이어 벨트가 있습니다. 컨베이어 벨트 위아래로 n개씩 총 2 * n 개의 숫자가 두 줄로 적혀 있고, 1초에 한 칸씩 움직입니다.t초의 시간이 흐른 뒤 컨베이어 벨트에 놓여있는 숫자들의 상태를 출력하는 프로그램을 작성해보세요.</code></p>
<ol>
<li><p>위에서 가장 오른쪽에 있는 숫자를 따로 temp값에 저장한다.</p>
<center><img src="https://velog.velcdn.com/images/dorahee-ee/post/21689cce-fc5d-4fea-8569-88b965faa12f/image.png" width="50%" height="50%"></center>
</li>
<li><p>위에 있는 숫자들을 완성한다. 오른쪽에서부터 채워넣어야 하며, 맨 왼쪽 숫자는 아래에서 가져온다.</p>
<center><img src="https://velog.velcdn.com/images/dorahee-ee/post/956a3e35-0ebb-48eb-bb99-7fe3f801cbe1/image.png" width="50%" height="50%"></center>
</li>
<li><p>아래에 있는 숫자들을 완성한다. 마찬가지로 오른쪽에서부터 채워야 하며, 맨 왼쪽 숫자는 <code>temp</code>값을 가져온다.</p>
<center><img src="https://velog.velcdn.com/images/dorahee-ee/post/5c8c051a-7bdc-4aa5-b310-b4b68b34992e/image.png" width="50%" height="50%"></center>

</li>
</ol>
<h4 id="💻-code-1">💻 CODE</h4>
<pre><code class="language-python">n, t = tuple(map(int, input().split()))
u = list(map(int, input().split()))
d = list(map(int, input().split()))

for _ in range(t):
    temp = u[n - 1]
    for i in range(n - 1, 0, -1):
        u[i] = u[i - 1]
    u[0] = d[n - 1]

    for i in range(n - 1, 0, -1):
        d[i] = d[i - 1]
    d[0] = temp

for elem in u:
    print(elem, end=&quot; &quot;)
print()

for elem in d:
    print(elem, end=&quot; &quot;)</code></pre>
<h2 id="격자-안에서-터지고-떨어지는-경우">격자 안에서 터지고 떨어지는 경우</h2>
<p>격자 안에서 터지고 떨어지는 작업이란, 특정 규칙에 의해 폭탄이 터지게 되고, 그 이후에 <strong>중력</strong>이 작용하여 위에 떠있는 폭탄들이 아래로 떨어지는 작업을 말한다.</p>
<p>$\rightarrow$ 시간복잡도 $O(n)$에 진행할 수 있다.</p>
<h3 id="2차원-격자에서-떨어지는-경우">2차원 격자에서 떨어지는 경우</h3>
<center><img src="https://velog.velcdn.com/images/dorahee-ee/post/eb20bf8a-542b-4d59-811d-036e314427d0/image.png" width="50%" height="50%"></center>

<ol>
<li><code>temp</code>라는 새로운 배열을 생성한다.</li>
<li>아래에서 위로 올라오면서, 비어있지 않을 때만 <code>temp</code>에 넣어준다.</li>
<li><code>temp</code>값을 기존 배열에 다시 옮긴다.</li>
</ol>
<h3 id="1차원-격자에서-떨어지는-경우">1차원 격자에서 떨어지는 경우</h3>
<center><img src="https://contents.codetree.ai/problems/17/images/64e9c35a-d466-48aa-b457-52bdd440758c.png" width="50%" height="50%"></center>

<ol>
<li><code>temp</code>라는 새로운 배열을 생성한다.</li>
<li>왼쪽에서 오른쪽으로 가면서, 비어있지 않을 때만 <code>temp</code>에 넣어준다.</li>
<li><code>temp</code>값을 기존 배열에 다시 옮긴다.</li>
</ol>
<pre><code>-&gt; 1차원과 2차원에서의 전체적인 흐름은 서로 비슷하다.</code></pre><h2 id="격자-안에서-단일-객체를-이동">격자 안에서 단일 객체를 이동</h2>
<p>격자에서 인접한 4방향으로 이동하는 문제에서는 코드를 깔끔하게 작성하기 위해 <code>dx, dy 테크닉</code>을 이용하는 것이 좋다.</p>
<h3 id="✏️-연습문제--숫자가-더-큰-인접한-곳으로-이동">✏️ 연습문제 : 숫자가 더 큰 인접한 곳으로 이동</h3>
<p><code>1이상 100이하의 숫자로 이루어진 n * n 크기의 격자판 정보가 주어집니다. 이때 특정 위치에서 시작하여, 상하좌우로 인접한 곳에 있는 숫자들 중 현재 위치에 있는 숫자보다 더 큰 위치로 끊임없이 이동합니다. 만약 그러한 위치가 여러개 있는 경우, 상하좌우 방향 순서대로 우선순위를 매겨 가능한 곳 중 우선순위가 더 높은 곳으로 이동합니다. 격자를 벗어나서는 안되며, 더 이상 움직일 수 없을 때까지 반복합니다.
위의 규칙에 따라 방문하게 되는 위치에 적힌 숫자를 순서대로 출력하는 프로그램을 작성해보세요.</code></p>
<ul>
<li>움직일 수 있는 곳이 여러 곳일 경우, 먼저 움직여야 하는 방향이 정해져 있기 때문에 우선적으로 봐야하는 방향의 순서는 상하좌우이므로, <code>dx</code>, <code>dy</code> 값을 상하좌우 순서대로 적어주면 편하다.</li>
</ul>
<ol>
<li><p><code>(Next X, Next Y)</code>가 격자를 벗어나는 경우 해당 방향에 인접한 숫자가 존재하지 않으므로, 그 다음 방향이 가능한지 확인한다.</p>
</li>
<li><p><code>(Next X, Next Y)</code>가 격자를 벗어나지 않는 경우</p>
<ol>
<li>이동하려는 곳에 적혀있는 값이 더 큰 경우 조건을 만족시키므로 이동한다.</li>
<li>이동하려는 곳에 적혀있는 값이 같거나 작은 경우 조건을 충족시키지 못했으므로 이동하지 않고, 그 다음 바향이 가능한지 확인한다.</li>
</ol>
</li>
</ol>
<h4 id="💻-code-2">💻 CODE</h4>
<pre><code class="language-python">n, curr_x, curr_y = tuple(map(int, input().split()))
a = [[0] * (n + 1)]
for _ in range(n):
    a.append([0] + list(map(int, input().split())))

visited_nums = []

def in_range(x, y):
    return 1 &lt;= x and x &lt;= n and 1 &lt;= y and y &lt;= n

def can_go(x, y, curr_num):
    return in_range(x, y) and a[x][y] &gt; curr_num

def simulate():
    global curr_x, curr_y
    dxs, dys = [-1, 1, 0, 0], [0, 0, -1, 1]

    for dx, dy in zip(dxs, dys):
        next_x, next_y = curr_x + dx, curr_y + dy

        if can_go(next_x, next_y, a[curr_x][curr_y]):
            curr_x, curr_y = next_x, next_y
            return True

    return False

visited_nums.append(a[curr_x][curr_y])
while True:
    greater_number_exist = simulate()
    if not greater_number_exist:
        break
    visited_nums.append(a[curr_x][curr_y])

for num in visited_nums:
    print(num, end=&#39; &#39;)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[코드트리] dx dy technique]]></title>
            <link>https://velog.io/@dorahee-ee/%EC%BD%94%EB%93%9C%ED%8A%B8%EB%A6%AC-dx-dy-technique</link>
            <guid>https://velog.io/@dorahee-ee/%EC%BD%94%EB%93%9C%ED%8A%B8%EB%A6%AC-dx-dy-technique</guid>
            <pubDate>Thu, 10 Aug 2023 05:46:50 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>학교에서 진행하는 여름방학 코딩테스트 대비 캠프 <em>with</em> <a href="https://www.codetree.ai/">CodeTree</a> 의 강의와 문제풀이를 정리한 내용입니다.</p>
</blockquote>
<h1 id="dx-dy-technique이란">dx dy technique이란?</h1>
<pre><code class="language-(x,">숫자 0이 주어지면 동쪽으로, 
숫자 1이 주어지면 남쪽으로,
숫자 2가 주어지면 서쪽으로, 
숫자 3이 주어지면 북쪽으로 이동하려 합니다.</code></pre>
<p>다음과 같은 문제가 주어진다면 <code>x, y의 좌표</code>를 이동시키며 비슷한 형태의 코드를 반복적으로 사용하게 되어 실수를 유발할 수 있기 때문에 이를 해결하기 위해 <strong>dx dy technique</strong>을 사용한다.
<img src="https://velog.velcdn.com/images/dorahee-ee/post/bac87de5-ced1-4b54-be1d-a67c10e7b82c/image.png" width="50%" height="50%"></p>
<pre><code class="language-python">dir_num = 2 # 시작 방향
x, y = 1, 5 # 시작 좌표
dx, dy = [1, 0, -1, 0], [0, -1, 0, 1]

nx, ny = x + dx[dir_num], y + dy[dir_num] # 이동할 좌표</code></pre>
<p>다음과 같이 각 방향에 맞는 x 좌표의 차를 <code>dx</code>에 y 좌표의 차를 <code>dy</code>에 적어줌으로써 깔끔한 코드 작성이 가능해진다.</p>
<hr>
<h2 id="방향-회전을-위한-dx-dy-정의-방법">방향 회전을 위한 dx, dy 정의 방법</h2>
<pre><code>(1, 5) 위치에서 시작하며 현재 북쪽을 바라보고 있습니다. 

방향을 시계방향으로 90&#39; 회전한 후, 
앞으로 한 칸 이동한 이후의 위치를 구해보세요.</code></pre><p>다음과 같은 문제가 주어졌을 때, 위에서 했던 방법으로 <code>dir_num</code>을 정의하게 된다면 문제가 복잡해지게 된다.
<img src="https://velog.velcdn.com/images/dorahee-ee/post/84e7e542-d7c7-4345-ae5e-c75c3a514797/image.png" width="50%" height="50%"></p>
<p>다음과 같이 <code>시계 방향 순서대로</code> 정의를 하게 된다면 <strong>dy, dy에서 방향 번호를 시계방향 순서대로 적어 넣었다 보니, 결국 시계방향으로 90&#39; 회전하는 것은 <code>dir_num</code>을 1 증가시키는 것만으로도 가능하다는 것을 알 수 있다.</strong>
<img src="https://velog.velcdn.com/images/dorahee-ee/post/0bf7d1eb-10ed-4dce-a79e-6f80cefd7ef3/image.png" width="50%" height="50%"></p>
<p><em>하지만, <code>dir_num</code>이 3인 경우에는 다시 0이 되어야 하므로 현재 방향이 dir_num이었다면 90&#39; 회전한 이후의 방향은 <code>(dir_num + 1) % 4</code>임을 알 수 있다.</em></p>
<pre><code class="language-python">dir_num = 3 # 시작 방향
x, y = 1, 5 # 시작 좌표
dx, dy = [1, 0, -1, 0], [0, -1, 0, 1] # 시계방향 순서

dir_num = (dir_num + 1) % 4 # 회전할 방향

nx, ny = x + dx[dir_num], y + dy[dir_num] # 다음 위치</code></pre>
<p><em>반시계 방향에 대한 회전은, 현재 <code>dir_num</code>에서 1을 빼주면 되는데 상황에 따라 음수가 되는 경우가 있기 때문에 항상 양수를 만들어 주는 것이 중요하다.</em></p>
<pre><code class="language-python">dir_num = (dir_num - 1 + 4) % 4</code></pre>
<hr>
<h2 id="격자에서의-dy-dy">격자에서의 dy, dy</h2>
<p>격자가 주어진 경우 격자를 2차원 배열로 표현하여 문제를 해결할 수 있다. 격자에서 역시 <code>dx, dy 테크닉</code>을 이용하면 인접한 상하좌우의 위치를 쉽게 구할 수 있습니다.
<strong>다만, 여기서 유의해야할 점은 격자에서의 x, y를 수학에서의 x, y가 아닌 행, 열로 생각해야 한다는 것이다.</strong>
(x, y)의 의미를 x행 y열로 생각하면, 다음과 같이 dx, dy를 정의할 수 있다.</p>
<center><img src="https://velog.velcdn.com/images/dorahee-ee/post/afff8fcb-3b0e-4e47-aebb-38eee6f58c0c/image.png" width="50%" height="50%"></center>

<p>여기서 다음과 같이 <code>zip</code> 함수를 사용하여 <code>dxs</code>, <code>dys</code>로 작성하게 되면 가독성이 좋게 코드를 작성할 수 있다. </p>
<pre><code class="language-python">x, y = 2, 1
dxs, dys = [0, 1, 0, -1], [1, 0, -1, 0]

cnt = 0
for dx, dy in zip(dxs, dys):
    nx, ny = x + dx, y + dy
    if a[nx][ny] == 1:
        cnt += 1</code></pre>
<p><em>하지만, 이렇게만 코드를 작성하게 되면 index가 범위를 벗어났을 경우 <code>Runtime Error</code>가 발생하게 된다.</em>
이 문제를 극복하기 위해 <strong>인접한 위치가 격자 안에 들어오는지를 판단하는 <code>in_range(x, y)</code> 함수</strong>를 작성해준다.</p>
<pre><code class="language-python"># (x, y)가 격자 안에 들어오는 경우에만 True, 그렇지 않다면 False를 반환하는 함수이다.
def in_range(x, y):
    return 0 &lt;= x and x &lt; n and 0 &lt;= y and y &lt; n</code></pre>
<h3 id="✏️-연습문제--1이-3개-이상-있는-위치">✏️ 연습문제 : 1이 3개 이상 있는 위치</h3>
<p><code>숫자 0과 1로만 이루어진 n * n 크기의 격자 상태가 주어집니다. 각 칸 중 상하좌우로 인접한 칸 중 숫자 1이 적혀 있는 칸의 수가 3개 이상인 곳의 개수를 세는 프로그램을 작성해보세요. 단, 인접한 곳이 격자를 벗어나는 경우에는 숫자 1이 적혀있지 않은 것으로 생각합니다.</code></p>
<h4 id="💻-code">💻 CODE</h4>
<pre><code class="language-python">n = int(input())
arr = [list(map(int, input().split())) for _ in range(n)]

def in_range(x, y):
    return 0 &lt;= x and x &lt; n and 0 &lt;= y and y &lt; n

dxs, dys = [0, 1, 0, -1], [1, 0, -1, 0]

total_cnt = 0
for i in range(n):
    for j in range(n):
        cnt = 0
        for dx, dy in zip(dxs, dys):
            nx, ny = j + dx, i + dy
            if in_range(nx, ny) and arr[nx][ny] == 1:
                cnt += 1

        if cnt &gt;= 3:
            total_cnt += 1

print(total_cnt)</code></pre>
<p>여기서 주의할 부분은 <code>if in_range(nx, ny) and arr[nx][ny] == 1:</code> 이다.
만약 <code>if a[nx][ny] == 1 and in_range(nx, ny)</code> 라고 적는다면 <code>nx</code>, <code>ny</code>의 값이 범위를 넘어간다면 <strong>IndexError</strong>를 유발하게 된다.
<strong>따라서 in_range와 같이 범위를 확인하는 조건의 경우에는 항상 if문 가장 앞에 작성하는 것이 중요하다.</strong></p>
<hr>
<h2 id="조건에-따라-방향이-변하는-경우">조건에 따라 방향이 변하는 경우</h2>
<pre><code>n * n 크기의 격자 위 (x, y) 위치에서 공이 상하좌우 중 한 방향으로 이동중입니다.
이동 도중 격자 끝에 다다르면, 방향을 반대로 바꿔 다시 움직입니다.
1초에 한 칸씩 움직인다 했을 때, 10초 뒤 공의 위치는 어떻게 되나요?</code></pre><p>다음과 같은 문제 또한 방향에 따라 움직이는 문제이므로 <code>dx, dy 테크닉</code>을 사용할 수 있다.
<em>단, 격자 끝에 도착하면 방향을 반대로 뒤집어 줘야 하기 때문에 방향 번호를 잘 정의해주는 것이 중요하다.</em></p>
<center><img src="https://velog.velcdn.com/images/dorahee-ee/post/8a5b9511-38cc-42c7-9f8b-bdf810599bb9/image.png" width="50%" height="50%"></center>

<p>0번과 3번이 반대 방향이 되도록하고, 1번과 2번이 반대 방향이 되도록 설정했기 때문에 방향을 뒤집는 작업은 3에서 현재 방향 번호를 빼주면 된다.</p>
<pre><code class="language-python">n = 5
x, y = 1, 2
dir_num = 2

dxs, dys = [0, 1, -1, 0], [1, 0, 0, -1]

def in_range(x, y):
    return 0 &lt;= x and x &lt; n and 0 &lt;= y and y &lt; n

while keep_moving():
    nx, ny = x + dxs[dir_num], y + dys[dir_num]
    if not in_range(nx, ny):  
        dir_num = 3 - dir_num # change direction

    x, y = x + dxs[dir_num], y + dys[dir_num]</code></pre>
<p>다음과 같이 코드를 작성해볼 수 있는데, <strong>만약 움직여야 할 방향이 &#39;R&#39;, &#39;D&#39;, &#39;U&#39;, &#39;L&#39;과 같이 문자로 주어진 경우라면 <code>dict</code>를 이용하여 방향과 방향 번호를 <code>key-value</code> 형태로 저장하여 문제를 해결할 수 있다.</strong></p>
<h3 id="✏️-연습문제--작은-구슬의-이동">✏️ 연습문제 : 작은 구슬의 이동</h3>
<p><code>구슬의 처음 위치와 초기 방향이 주어졌을 때, t초가 지난 이후에 해당 구슬의 위치를 구하는 프로그램을 작성해보세요.</code></p>
<h4 id="💻-code-1">💻 CODE</h4>
<pre><code class="language-python">n, t = map(int, input().split())
r, c, d = input().split()

dxs, dys = [0, 1, -1, 0], [1, 0, 0, -1]
mapper = {
    &#39;R&#39;: 0,
    &#39;D&#39;: 1,
    &#39;U&#39;: 2,
    &#39;L&#39;: 3
}
x, y, move_dir = int(r) - 1, int(c) - 1, mapper[d]

def in_range(x, y):
    return 0 &lt;= x and x &lt; n and 0 &lt;= y and y &lt; n

for _ in range(t):
    nx, ny = x + dxs[move_dir], y + dys[move_dir]
    if in_range(nx, ny):
        x, y = nx, ny
    else:
        move_dir = 3 - move_dir  # 방향전환

print(x + 1, y + 1)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Mac] Setup M1 MacBook for Machine Learning]]></title>
            <link>https://velog.io/@dorahee-ee/Mac-Setup-M1-MacBook-for-Machine-Learning</link>
            <guid>https://velog.io/@dorahee-ee/Mac-Setup-M1-MacBook-for-Machine-Learning</guid>
            <pubDate>Sat, 17 Jun 2023 17:46:39 GMT</pubDate>
            <description><![CDATA[<h1 id="📍anaconda">📍ANACONDA</h1>
<center><img src="https://velog.velcdn.com/images/dorahee-ee/post/b7f0319d-ed93-41a7-a318-98432b6181f0/image.png" width=50% height=50%></center>

<p><strong>Anaconda</strong>는 주로 <code>Python</code>과 <code>R</code>에서 패키지 관리와 패키지 버전 관리를 용이하게 하기 위해서 사용한다. 주로 가상환경을 만들어서 <code>가상환경별로 독립적인 패키지 버전 관리</code>가 가능하다는 장점이 있다.</p>
<p>아나콘다 설치 후 initialize가 된 상태에서 가상환경을 따로 만들지 않은 경우 <code>base</code> 환경이라고 표현합니다.</p>
<ul>
<li><p><code>base</code>에서 pytorch가 설치되어 있지 않고,</p>
</li>
<li><p><code>A</code>라는 가상환경을 아나콘다로 만들어 1.2.0 버전의 pytorch를 설치하고,</p>
</li>
<li><p><code>B</code>라는 가상환경에선 1.8.0 버전을 설치했으면,</p>
</li>
</ul>
<p>우리는 <code>base</code>와 <code>A</code>, <code>B</code>라는 각각 독립적으로 다른 환경을 사용할 수 있게 되는 것이다. 그리고 이 각각의 가상환경들은 각자의 패키지 버전에 영향을 받지 않는다.
특히, 딥러닝 연구자의 입장에선 다양한 프로젝트의 코드를 관리하기 위해 없어서는 안되는 중요한 역할을 한다.</p>
<h2 id="👩🏻💻-anaconda-설치">👩🏻‍💻 Anaconda 설치</h2>
<h4 id="✏️-anaconda-다운로드">✏️ Anaconda 다운로드</h4>
<p><a href="https://www.anaconda.com/">https://www.anaconda.com/</a> 에서 Mac에 호환되는 파일을 다운받고, 파일을 클릭하여 설치를 진행한다.</p>
<center><img src="https://velog.velcdn.com/images/dorahee-ee/post/da000837-9da4-4634-b47a-1770db0409fa/image.png" width="70%" height="70%"></center>

<center><img src="https://velog.velcdn.com/images/dorahee-ee/post/cd194409-a96e-4e09-b5fc-3bfbbbd63421/image.png
" width="70%" height="70%"></center>

<h4 id="✏️-conda-설정-반영하기">✏️ Conda 설정 반영하기</h4>
<p>설치가 끝난 후, conda 명령어를 쓰려고 하면 다음과 같은 에러가 난다. </p>
<pre><code>% conda --help 
zsh: command not found: conda</code></pre><p>아나콘다 설치 폴더가 환경변수에 제대로 등록되지 않았기 때문에 발생하는 문제이다. 이 경우 다음과 같은 명령어를 통해 환경변수를 적용시켜준다.</p>
<pre><code>% source ~/.zshrc</code></pre><p>환경변수 적용 후 다시 실행하면 정상적으로 작동하여 <code>conda 명령어</code>를 사용할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/89b32166-1057-4e0c-bb32-8ba581cf40a1/image.png" alt=""></p>
<hr>
<h1 id="📍pytorch">📍PYTORCH</h1>
<center><img src="https://velog.velcdn.com/images/dorahee-ee/post/b601e4ca-32df-4375-908b-104d205207c5/image.png" width="70%" height="70%"></center>

<p><strong>Pytorch</strong>는 페이스북의 인공지능 연구팀이 개발한 파이썬 기반 오픈소스 라이브러리인 <code>Torch</code>에 바탕을 두고 만들어진 프레임워크이다. 코드를 깔끔하고 직관적으로 작성할 수 있다는 장점이 있으며, 그래프를 만들면서 동시에 값을 할당하는 Define by run 방식을 사용한다. 
<code>TensorFlow</code>는 신경망을 유기적으로 만들 수 없다는 성능의 한계가 있지만, <code>Pytorch</code>는 메모리에서 연산 하면서도 신경망 사이즈도 최적으로 동작시킬 수 있다는 강점이 있고, 학습 속도도 더 빠르다. <code>Pytorch</code>는 Numpy를 대체할 수 있으며 GPU를 이용한 연산도 가능하므로, 최근 딥러닝 관련 논문들이나 업계에서도 텐서플로우보다는 연산도 가능하고 속도도 빠른 Pytorch를 선호하는 추세이다.</p>
<h2 id="👩🏻💻-pytorch-설치">👩🏻‍💻 Pytorch 설치</h2>
<h4 id="✏️-pytorch-다운로드">✏️ Pytorch 다운로드</h4>
<p><a href="https://pytorch.org/get-started/locally/">https://pytorch.org/get-started/locally/</a> 에서 본인에게 맞는 버전을 택하고 복사하여 터미널에 붙여 넣는다.
<img src="https://velog.velcdn.com/images/dorahee-ee/post/be92dbae-520a-4278-b172-04e194016db3/image.png" alt=""></p>
<pre><code># MPS acceleration is available on MacOS 12.3+
% conda install pytorch::pytorch torchvision torchaudio -c pytorch</code></pre><h4 id="✏️-gpu-가속하기-feat-mps">✏️ GPU 가속하기 (Feat. MPS)</h4>
<p>NVIDIA GPU에서 <code>cuda</code> 장치를 사용하는 것처럼, Apple Silicon에서는 <code>mps</code> 장치를 사용한다. <code>mps</code>는 Apple의 <code>Metal Performance Shaders Framework</code>를 사용하기 때문에 붙여진 이름이다.</p>
<p>현재 설치된 PyTorch 빌드가 mps backend를 지원하는지는 다음과 같이 확인해보실 수 있다.</p>
<pre><code class="language-python">&gt;&gt;&gt; import torch
&gt;&gt;&gt; print(torch.__version__)  # pytorch 버전 확인
&gt;&gt;&gt; print(torch.backends.mps.is_available())  # 현재 기기에 사용 가능한 MPS 장치가 있는지 확인 (= torch.cuda.is_available())
&gt;&gt;&gt; print(torch.backends.mps.is_built())  # 현재 설치된 PyTorch 빌드가 mps backend를 지원하는지 확인</code></pre>
<pre><code>2.0.1
True
True</code></pre><p>만약 <code>torch.backends.mps.is_built()</code>의 결과가 <code>True</code>가 아니라면, conda 또는 PyTorch를 다시 설치해야 한다.</p>
<p><code>cuda</code> 장치와 마찬가지로 <code>torch.device(&quot;mps&quot;)</code>와 같이 <code>mps</code> 장치를 지정한 뒤, Tensor나 모델을 이동하고 사용할 수 있다.
아래는 <code>PyTorch 공식 문서</code>의 예시 코드이다.</p>
<pre><code class="language-python">mps_device = torch.device(&quot;mps&quot;)

# MPS 장치에 바로 tensor를 생성합니다.
x = torch.ones(5, device=mps_device)
# 또는
x = torch.ones(5, device=&quot;mps&quot;)

# GPU 상에서 연산을 진행합니다.
y = x * 2

# 또는, 다른 장치와 마찬가지로 MPS로 이동할 수도 있습니다.
model = YourFavoriteNet()  # 어떤 모델의 객체를 생성한 뒤,
model.to(mps_device)       # MPS 장치로 이동합니다.

# 이제 모델과 텐서를 호출하면 GPU에서 연산이 이뤄집니다.
pred = model(x)</code></pre>
<hr>
<h1 id="📍tensorflow">📍TENSORFLOW</h1>
<center><img src="https://velog.velcdn.com/images/dorahee-ee/post/fd575076-7fc1-4bdf-87d6-f952c74ea260/image.png
" width="300" height="300"></center>

<p><strong>TensorFlow</strong>는 구글(Google)에서 만든, 딥러닝 프로그램을 쉽게 구현할 수 있도록 다양한 기능을 제공해주는 라이브러리다. Python을 최우선으로 지원하며 대부분의 편한 기능들이 파이썬 라이브러리로만 구현되어 있어 Python에서 개발하는 것이 편하다. <code>TensorFlow</code>를 사용하는 가장 큰 장점은 추상화에 있고, 디버깅과 시각화에 매우 능한 플랫폼이다.</p>
<h2 id="👩🏻💻-tensorflow-설치">👩🏻‍💻 TensorFlow 설치</h2>
<h4 id="✏️-tensorflow-다운로드">✏️ TensorFlow 다운로드</h4>
<ol>
<li>TensorFlow dependencies 설치<pre><code class="language-bash">conda install -c apple tensorflow-deps</code></pre>
</li>
<li>TensorFlow 설치<pre><code class="language-bash">python -m pip install tensorflow-macos</code></pre>
</li>
<li>TensorFlow-Metal (GPU framework) 설치<pre><code class="language-bash">python -m pip install tensorflow-metal</code></pre>
TensorFlow 설치는 완료하였고, 제대로 설치되었는지 확인한다.<pre><code class="language-python">import tensorflow as tf
print(&quot;Num GPUs Available: &quot;, len(tf.config.experimental.list_physical_devices(&#39;GPU&#39;)))</code></pre>
다음과 같은 OUTPUT이 나오면 GPU가 제대로 작동하는 것이다.<pre><code>Num GPUs Available: 1</code></pre>GPU를 이용하여 모델의 학습을 더 빠르게 진행할 수 있다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 기능개발]]></title>
            <link>https://velog.io/@dorahee-ee/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B8%B0%EB%8A%A5%EA%B0%9C%EB%B0%9C</link>
            <guid>https://velog.io/@dorahee-ee/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B8%B0%EB%8A%A5%EA%B0%9C%EB%B0%9C</guid>
            <pubDate>Sun, 08 Jan 2023 05:50:28 GMT</pubDate>
            <description><![CDATA[<h1 id="📍문제">📍문제</h1>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/42586">[프로그래머스] Lv.2 기능개발</a>
<img src="https://velog.velcdn.com/images/dorahee-ee/post/a67df1c3-05a8-4474-877f-28c7e5bd92ae/image.png" alt=""></p>
<hr>
<h1 id="✏️-풀이">✏️ 풀이</h1>
<h3 id="✔️-큐queue">✔️ 큐(Queue)</h3>
<ul>
<li>먼저 넣은 데이터를 먼저 반환하도록 설계된 메모리 구조</li>
<li><code>First In Fisrt Out (FIFO)</code></li>
<li><code>Stack</code>과 반대되는 개념
<img src="https://velog.velcdn.com/images/dorahee-ee/post/f5f44be4-0d76-44da-a14b-f5e341005d42/image.png" alt=""></li>
</ul>
<h4 id="리스트를-사용하여-큐-구조-구현하기">리스트를 사용하여 큐 구조 구현하기</h4>
<pre><code class="language-python">a = [1, 2, 3, 4, 5]
a.append(10)  # 큐의 put = append()
a.pop()  # 큐의 get = pop(0) -&gt; 맨 앞의 요소가 삭제 및 출력
# pop(0) -&gt; 0번째 요소 삭제 및 출력</code></pre>
<hr>
<h1 id="💻-code">💻 CODE</h1>
<pre><code class="language-python">def solution(progresses, speeds):
    answer = []
    while len(progresses) &gt; 0:
        cnt = 0
        while len(progresses) &gt; 0 and progresses[0] &gt;= 100:
            cnt += 1
            progresses.pop(0)
            speeds.pop(0)
        progresses = [progresses[i] + speeds[i] for i in range(len(progresses))]

        if cnt &gt; 0:
            answer.append(cnt)

    return answer</code></pre>
<h3 id="✔️-큐를-사용한-문제-해결-과정">✔️ 큐를 사용한 문제 해결 과정</h3>
<h4 id="작업-리스트와-개발-속도를-큐라고-생각하고">작업 리스트와 개발 속도를 큐라고 생각하고,</h4>
<ol>
<li><code>progresses</code>가 비었다면 멈춘다.<ol start="2">
<li>배포된 기능의 개수 초기화 (<code>cnt = 0</code>)</li>
<li><code>proegresses</code>의 맨 앞 작업을 확인한다.<ol start="4">
<li>해당 작업이 완료되었다면(100% 이상) <code>progresses</code>와 <code>speeds</code>에서 제거하고 배포된 기능 개수를 증가시킨다. (<code>cnt += 1</code>) -&gt; <em>3으로 돌아간다</em></li>
</ol>
</li>
<li>해당 작업이 완료되지 않았다면(100% 미만) 작업리스트의 진도를 업데이트한다.</li>
<li>만약 오늘 배포된 기능 개수가 0이 아니라면 결과 리스트에 추가한다. -&gt; <em>1로 돌아간다</em></li>
</ol>
</li>
</ol>
<hr>
<h2 id="🖥-다른-사람-풀이">🖥 다른 사람 풀이</h2>
<pre><code class="language-python">import math

def solution(progresses, speeds):
    progresses = [math.ceil((100 - a) / b) for a, b in zip(progresses, speeds)]
    answer = []
    front = 0, 0

    for idx in range(len(progresses)):
        if progresses[idx] &gt; progresses[front]:  
            answer.append(idx - front)
            front = idx 
    answer.append(len(progresses) - front)  

    return answer</code></pre>
<h3 id="✔️-mathceil">✔️ math.ceil()</h3>
<blockquote>
<p><code>math.ceil()</code> 함수는 실수를 입력하면 올림하여 정수를 반환하는 함수이다.
ex) math.ceil(3.14) = 4</p>
</blockquote>
<ol>
<li><p><code>math.ceil()</code>을 활용하여 소요시간을 각각 구한다.
&rightarrow; <code>progresses = [7, 3, 9]</code></p>
</li>
<li><p><code>progresses</code>의 각 소요시간을 확인하는데 이때 <code>front</code>에 가장 오래걸린 소요 시간의 인덱스를 저장해둔다.</p>
</li>
<li><p><code>idx = 0</code> : 첫번째 수(7) 보다 첫번째 수 (7) 는 같기 때문에 pass &rightarrow; <code>front = 0</code></p>
</li>
<li><p><code>idx = 1</code> : 첫번째 수(7) 보다 두번째 수 (3) 는 같기 때문에 pass &rightarrow; <code>front = 0</code></p>
</li>
<li><p><code>idx = 2</code> : 첫번째 수(7) 보다 세번째 수(9) 는 크기 때문에 현재 인덱스부터 프론트인덱스의 차를 구하고 이를 answer에 append (그 전까지 친구들은 동시 출시니까) 프론트인덱스를 현재인덱스로 업데이트! &rightarrow; <code>front = 2</code></p>
</li>
<li><p>맨 마지막에 현재 프론트인덱스와 전체 길이의 차를 구해서 남은 친구들 다 출시 </p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 올바른 괄호]]></title>
            <link>https://velog.io/@dorahee-ee/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%98%AC%EB%B0%94%EB%A5%B8-%EA%B4%84%ED%98%B8</link>
            <guid>https://velog.io/@dorahee-ee/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%98%AC%EB%B0%94%EB%A5%B8-%EA%B4%84%ED%98%B8</guid>
            <pubDate>Sat, 07 Jan 2023 13:58:06 GMT</pubDate>
            <description><![CDATA[<h1 id="📍문제">📍문제</h1>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/12909">[프로그래머스] Lv.2 올바른 괄호</a>
<img src="https://velog.velcdn.com/images/dorahee-ee/post/b9349827-d675-4c52-ae2f-a07fcf3f1963/image.png" alt=""></p>
<hr>
<h1 id="✏️-풀이">✏️ 풀이</h1>
<h3 id="✔️-신찬수-교수님의-pseudo-코드">✔️ 신찬수 교수님의 Pseudo 코드</h3>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/3c5c3f5f-b3f5-481e-a03a-e945a9251c98/image.png" alt=""></p>
<hr>
<h1 id="💻-code">💻 CODE</h1>
<pre><code class="language-python">def solution(s):
    stack = []
    for i in s:
        if i == &#39;(&#39;:
            stack.append(i)
        else:
            if len(stack) == 0:
                return False
            else:
                stack.pop()
    if len(stack) &gt; 0:
        return False
    return True</code></pre>
<ol>
<li><code>if i == &#39;(&#39;:</code>
열린 괄호를 넣어준다.</li>
<li><code>else: #닫힌 괄호</code></li>
</ol>
<ul>
<li>스택안에 아무것도 없는 경우 -&gt; 짝이 맞지 않기 때문에 <code>return False</code></li>
<li>스택안에 있는 경우 -&gt; stack의 열린 괄호를 <code>pop</code> 한다.</li>
</ul>
<ol start="3">
<li>For문을 다 돌고 난 뒤에도 stack안에 남아있는 괄호가 있다면 짝이 맞지 않는 것이기 때문에 <code>return False</code></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 같은 숫자는 싫어]]></title>
            <link>https://velog.io/@dorahee-ee/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B0%99%EC%9D%80-%EC%88%AB%EC%9E%90%EB%8A%94-%EC%8B%AB%EC%96%B4</link>
            <guid>https://velog.io/@dorahee-ee/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B0%99%EC%9D%80-%EC%88%AB%EC%9E%90%EB%8A%94-%EC%8B%AB%EC%96%B4</guid>
            <pubDate>Sat, 07 Jan 2023 13:35:14 GMT</pubDate>
            <description><![CDATA[<h1 id="📍문제">📍문제</h1>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/12906">[프로그래머스] Lv.1 같은 숫자는 싫어</a></p>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/4242ba76-7620-40de-9cb8-18b0f22fdde8/image.png" alt=""></p>
<hr>
<h1 id="✏️-풀이">✏️ 풀이</h1>
<h3 id="✔️-스택stack">✔️ 스택(Stack)</h3>
<ul>
<li>나중에 넣은 데이터를 먼저 반환하도록 설계된 메모리 구조</li>
<li><code>Last In First Out (LIFO)</code></li>
<li>Data의 입력 = <code>Push</code>, 출력 = <code>Pop</code>
<img src="https://velog.velcdn.com/images/dorahee-ee/post/6f978898-3d5f-496c-bd28-fd498c87fa9d/image.png" alt=""></li>
</ul>
<h4 id="리스트를-사용하여-스택-구조-구현하기">리스트를 사용하여 스택 구조 구현하기</h4>
<pre><code class="language-python">a = [1, 2, 3, 4, 5]
a.append(10)  # 스택의 push = append()
a.pop()  # 스택의 pop = pop() -&gt; 맨 뒤의 요소가 삭제 및 출력</code></pre>
<h3 id="✔️-큐queue">✔️ 큐(Queue)</h3>
<ul>
<li>먼저 넣은 데이터를 먼저 반환하도록 설계된 메모리 구조</li>
<li><code>First In Fisrt Out (FIFO)</code></li>
<li><code>Stack</code>과 반대되는 개념
<img src="https://velog.velcdn.com/images/dorahee-ee/post/f5f44be4-0d76-44da-a14b-f5e341005d42/image.png" alt=""></li>
</ul>
<h4 id="리스트를-사용하여-큐-구조-구현하기">리스트를 사용하여 큐 구조 구현하기</h4>
<pre><code class="language-python">a = [1, 2, 3, 4, 5]
a.append(10)  # 큐의 put = append()
a.pop()  # 큐의 get = pop(0) -&gt; 맨 앞의 요소가 삭제 및 출력
# pop(0) -&gt; 0번째 요소 삭제 및 출력</code></pre>
<hr>
<h1 id="💻-code">💻 CODE</h1>
<pre><code class="language-python">def solution(arr):
    ans = []
    for i in range(len(arr)):
        if i == 0:
            ans.append(arr[i])
        elif arr[i] != arr[i-1]:
            ans.append(arr[i])
    return ans</code></pre>
<p>빈 배열을 만들어서 <code>arr[i]</code>와 <code>arr[i-1]</code>을 비교하여 리스트에 넣어준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 약수의 합]]></title>
            <link>https://velog.io/@dorahee-ee/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%95%BD%EC%88%98%EC%9D%98-%ED%95%A9</link>
            <guid>https://velog.io/@dorahee-ee/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%95%BD%EC%88%98%EC%9D%98-%ED%95%A9</guid>
            <pubDate>Sat, 07 Jan 2023 13:00:35 GMT</pubDate>
            <description><![CDATA[<h1 id="📍문제">📍문제</h1>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/12928">[프로그래머스] Lv.1 약수의 합</a>
<img src="https://velog.velcdn.com/images/dorahee-ee/post/f181b58c-ea18-4eb4-bc25-d359c89f2095/image.png" alt=""></p>
<hr>
<h1 id="✏️-풀이">✏️ 풀이</h1>
<h3 id="✔️-약수">✔️ 약수</h3>
<blockquote>
<p>약수란 어떤 수를 나누어 떨어지게 하는 수이다.
ex) 12의 약수 = [1, 2, 3, 4, 6, 12]</p>
</blockquote>
<hr>
<h1 id="💻-code">💻 CODE</h1>
<pre><code class="language-python">def solution(n):
    ans = 0
    for i in range(1, n+1):
        if n % i == 0:
            ans += i
    return ans</code></pre>
<p>for문을 이용하여 i가 n을 나누어 떨어지게 한다면 n의 약수이므로 ans에 더해준다.</p>
<hr>
<h2 id="🖥-다른-사람-풀이">🖥 다른 사람 풀이</h2>
<pre><code class="language-python">def solution(n):    
    return sum([i for i in range(1, n+1) if n % i == 0])</code></pre>
<p><code>list comprehension</code>을 이용하여 간결하게 풀 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 최대공약수와 최소공배수]]></title>
            <link>https://velog.io/@dorahee-ee/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%B5%9C%EB%8C%80%EA%B3%B5%EC%95%BD%EC%88%98%EC%99%80-%EC%B5%9C%EC%86%8C%EA%B3%B5%EB%B0%B0%EC%88%98</link>
            <guid>https://velog.io/@dorahee-ee/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%B5%9C%EB%8C%80%EA%B3%B5%EC%95%BD%EC%88%98%EC%99%80-%EC%B5%9C%EC%86%8C%EA%B3%B5%EB%B0%B0%EC%88%98</guid>
            <pubDate>Sat, 07 Jan 2023 10:29:52 GMT</pubDate>
            <description><![CDATA[<h1 id="📍문제">📍문제</h1>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/12940">[프로그래머스] Lv.1 최대공약수와 최소공배수</a>
<img src="https://velog.velcdn.com/images/dorahee-ee/post/117d644a-36f1-4f44-8506-308bc14a8529/image.png" alt=""></p>
<hr>
<h1 id="✏️-풀이">✏️ 풀이</h1>
<h3 id="✔️-유클리드-호제법">✔️ 유클리드 호제법</h3>
<blockquote>
<p>$a, b \in\mathbb{Z}$ 이고, $a$를 $b$로 나눈 나머지가 $r$이라고 하자. ($0 \leq r &lt;b \leq a$)
$a$와 $b$의 최대공약수를 $(a, b)$라고 하면, $(a, b) = (b, r)$ 이다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dorahee-ee/post/5c3045d7-36f6-4403-989e-c31ea4f476a0/image.png" alt=""></p>
<h3 id="✔️-두-수의-곱--최대공약수-x-최소공배수">✔️ 두 수의 곱 = 최대공약수 x 최소공배수</h3>
<ul>
<li>최소공배수 = 두 수의 곱 $\div$ 최대공약수</li>
<li>최대공약수 = 두 수의 곱 $\div$ 최소공배수</li>
</ul>
<hr>
<h1 id="💻-code">💻 CODE</h1>
<pre><code class="language-python">def solution(a, b):
    c, d = max(a, b), min(a, b)
    t = 1
    while t &gt; 0:
        t = c % d
        c, d = d, t
    answer = [c, int(a*b/c)]

    return answer</code></pre>
<h4 id="1-c-d--a-b-중-최대값-최소값">1. c, d = (a, b) 중 최대값, 최소값</h4>
<h4 id="2-유클리드-호제법">2. 유클리드 호제법</h4>
<ul>
<li>t = 나머지</li>
<li>t가 0이 될 때까지<h4 id="3-최소공배수--두-수의-곱-div-최대공약수">3. 최소공배수 = 두 수의 곱 $\div$ 최대공약수</h4>
</li>
</ul>
<hr>
<h2 id="🖥-다른-사람-풀이">🖥 다른 사람 풀이</h2>
<pre><code class="language-python">def solution(n, m):
    gcd = lambda a,b : b if not a%b else gcd(b, a%b)
    lcm = lambda a,b : a*b//gcd(a,b)
    return [gcd(n, m), lcm(n, m)]</code></pre>
<p><code>lambda</code> 와 <code>재귀함수</code>를 이용하면 훨씬 간결하게 풀 수 있다는 사실을 알게 되었다.</p>
]]></description>
        </item>
    </channel>
</rss>