<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>GURI_LOG</title>
        <link>https://velog.io/</link>
        <description>Done is better than Perfect. </description>
        <lastBuildDate>Sun, 07 Jul 2024 04:30:57 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>GURI_LOG</title>
            <url>https://velog.velcdn.com/images/suhyun-guri/profile/6a3aec19-f666-4278-9edc-048e6def2a80/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. GURI_LOG. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/suhyun-guri" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[KAFKA CONNECT (w/ group.id)]]></title>
            <link>https://velog.io/@suhyun-guri/KAFKA-CONNECT-w-group.id</link>
            <guid>https://velog.io/@suhyun-guri/KAFKA-CONNECT-w-group.id</guid>
            <pubDate>Sun, 07 Jul 2024 04:30:57 GMT</pubDate>
            <description><![CDATA[<h2 id="kafka-connect">KAFKA CONNECT</h2>
<p><img src="https://velog.velcdn.com/images/suhyun-guri/post/c0279b66-9c03-4a46-93b7-ae02946ebb88/image.png" alt="">
출처 : <a href="https://www.confluent.io/blog/kafka-connect-deep-dive-converters-serialization-explained/">https://www.confluent.io/blog/kafka-connect-deep-dive-converters-serialization-explained/</a></p>
<p>다양한 데이터 소스 시스템에서 발생한 데이터 이벤트를 다른 데이터 타겟 시스템으로 별도의 Kafka Client 코딩 없이 Seamless하게 실시간으로 전달하기 위해 만들어진 Kafka Component.
즉, Kafka Connect는 Kafka를 사용하여 다른 시스템과 데이터를 주고 받기 위한 오픈소스 프레임워크다. 다양한 내장 Connector를 제공하고 있다.</p>
<ul>
<li>Connector의 2가지 타입<ul>
<li>Source Connector - 외부 시스템에서 Kafka로 데이터를 넣어주는 Connector (외부 → 카프카)</li>
<li>Sink Connector - Kafka에서 데이터를 꺼내 외부 시스템에 데이터를 넣어주는 Connector (카프카 → 외부)</li>
</ul>
</li>
</ul>
<h2 id="kafka-connect의-구성요소">Kafka Connect의 구성요소</h2>
<ul>
<li>Connect Cluster - 여러 개의 Connect를 group.id로 묶어서 하나의 Connect Cluster가 됨</li>
<li>Connect<ul>
<li>Connect가 기동되면 JVM Process가 띄워짐. 이거를 워커가 함</li>
<li>그래서 Connect를 띄운다 = 워커 프로세스를 띄운다  (이 부분이 제일 헷갈렸던 부분)</li>
</ul>
</li>
<li>Connector <ul>
<li>REST API를 통해 Connect 위 기동을 시킬 수 있음.</li>
<li>Connect 위에 올라가는 Source와 Sink를 연결하는 컴포넌트</li>
</ul>
</li>
<li>Task <ul>
<li>커넥터들이 일을 수행하는 단위, Source와 직접 인터페이스를 하는 요소</li>
</ul>
</li>
<li>Thread <ul>
<li>Task는 Thread 단위로 구성됨. </li>
<li>소스에서 멀티 스레드가 가능하면 여러 개의 Task를 띄울 수 있음. </li>
<li>즉 Task = Thread</li>
</ul>
</li>
</ul>
<p>하나의 노드(인스턴스)에서 여러 개의 Connect / Connector를 띄울 수 있음 (대신 포트는 다르게 설정) 다른 노드에서 여러 개의 Connect를 띄워도 group.id로 묶으면 하나로 생성할 수 있음.</p>
<p><img src="https://velog.velcdn.com/images/suhyun-guri/post/7a33a1c4-52be-4ab8-90f7-c01d3cd421f2/image.png" alt=""></p>
<h2 id="흠-ok-근데-나는-워커를-두-개-이상-띄우고-싶어">흠.. OK, 근데 나는 워커를 두 개 이상 띄우고 싶어</h2>
<p>고가용성, 확장성을 위해 운영에서는 카프카 커넥트 워커를 한 개가 아닌 두 개 이상으로 운영할 것이다.</p>
<p>이 때, <strong>Group ID</strong>를 활용한다.</p>
<p>위에서 언급했듯이 Connect는 하나의 워커일 뿐이다.</p>
<p>그런데 우리가 운영을 할 때 워커를 한 개만 띄우면 고가용성, 확장성이 보장되지 않는다.</p>
<p>그럼 워커를 2개 이상을 띄우려면 어떻게 해야 하나?</p>
<ul>
<li>Connect 서버를 두 개 띄우면 된다.</li>
</ul>
<p>어 그러면 두 개의 서버가 서로 통신은 어떻게 하는거고 두 개의 워커가 어떻게 하나의 커넥터로서 작동하게 하나?</p>
<ul>
<li>이 연결 역할을 하는 것이 group.id 이다.</li>
</ul>
<p>Connect 설정의 group.id를 동일하게 부여하고 connect를 띄우면 (같은 kafka bootstrap일 때) 두 개의 connect는 하나의 connector에 대한 워커로서 그룹화가 된다.</p>
<p>이는 kafka connect를 분산 모드(distributed mode)로 띄웠을 때만 적용된다. 왜냐면 분산 모드일 때는 offset, config, status를 kafka cluster의 토픽 안에다 저장하므로 각 서버(워커)들이 동일한 토픽 데이터에 접근할 수 있다.</p>
<blockquote>
<p>이렇게 Connect(워커)를 2개 이상 띄우면, 멀티 스레드가 가능한 Connector라면 2개 이상의 워커들이 작동을 할 것이고 멀티 스레드를 지원하지 않는 Connector(ex. Debezium)라면 한 개만 작동하고 나머지는 메인 워커가 죽었을 때를 대비하는 대기열로 띄워져있는다.</p>
</blockquote>
<h2 id="주요-kafka-connect-configuration">주요 Kafka Connect configuration</h2>
<blockquote>
<p>참고 : <a href="https://kafka.apache.org/27/documentation.html#connectconfigs">https://kafka.apache.org/27/documentation.html#connectconfigs</a>
중요한 부분만 발췌하여 작성한다.</p>
</blockquote>
<p>group.id</p>
<ul>
<li>A unique string that identifies the Connect cluster group this worker belongs to.</li>
</ul>
<p>config.storage.topic / offset.storage.topic / status.storage.topic</p>
<ul>
<li>config, offset, status 정보를 저장할 토픽 이름 지정</li>
</ul>
<p>heartbeat.interval.ms (default : 3000 (3 seconds))</p>
<ul>
<li>카프카의 그룹 관리 기능을 사용할 때 group coordinator에게 보내는 heartbeat (살아있다고 신호보내는 것)</li>
<li>session.timeout.ms보다 낮게 설정해야 하지만 일반적으로 이 값의 1/3 이하로 설정하는 것이 좋다.</li>
</ul>
<p>rebalance.timeout.ms (default : 60000 (1 minute))</p>
<ul>
<li>리밸런싱이 시작된 후 각 작업자가 그룹에 참여할 수 있는 최대 허용 시간</li>
<li>시간 제한을 초과하면 작업자가 그룹에서 제거되어 오프셋 커밋이 실패</li>
</ul>
<p>session.timeout.ms (default : 10000 (10 seconds))</p>
<ul>
<li>워커 장애를 감지하는 데 사용되는 타임아웃. 워커는 주기적으로 하트비트를 전송하여 브로커에 활성 상태를 알린다.</li>
<li>이 세션 시간 제한이 만료되기 전에 브로커가 하트비트를 수신하지 못하면 브로커는 그룹에서 워커를 제거하고 재밸런싱을 시작</li>
</ul>
<p>connections.max.idle.ms (default : 540000 (9 minutes))</p>
<ul>
<li>이 구성에서 지정한 밀리초가 지나면 유휴 연결을 닫는다. (지정한 시간동안 아무 작업이 없으면 연결을 끊는다)</li>
</ul>
<p>listeners</p>
<ul>
<li>REST API가 수신 대기할 쉼표로 구분된 URI 목록. 지원되는 프로토콜은 HTTP와 HTTPS.</li>
<li>모든 인터페이스에 바인딩하려면 호스트 이름을 0.0.0.0으로, 기본 인터페이스에 바인딩하려면 값을 비워둔다.</li>
<li>ex) HTTP://myhost:8083,HTTPS://myhost:8084</li>
</ul>
<p>reconnect.backoff.max.ms (default : 1000 (1 second))</p>
<ul>
<li>반복적으로 연결에 실패한 브로커에 다시 연결할 때 대기할 수 있는 최대 시간(밀리초).</li>
<li>이 값을 제공하면 호스트당 백오프는 연결이 연속적으로 실패할 때마다 이 최대값까지 기하급수적으로 증가한다.</li>
<li>백오프 증가를 계산한 후에는 연결 폭주를 방지하기 위해 20%의 무작위 지터가 추가된다.</li>
</ul>
<p>reconnect.backoff.ms (default : 50)</p>
<ul>
<li>지정된 호스트에 다시 연결을 시도하기 전에 대기할 기본 시간. 호스트에 반복적으로 연결되는 것을 방지할 수 있다.</li>
<li>이 백오프는 클라이언트가 브로커에 연결하려는 모든 시도에 적용된다.</li>
</ul>
<p>rest.advertised.host.name / rest.advertised.port / rest.advertised.listener</p>
<ul>
<li>다른 워커에 전달하는 내 hostname, port, listener (protocol)</li>
</ul>
<p>retry.backoff.ms (default : 100)</p>
<ul>
<li>지정된 토픽 파티션에 실패한 요청을 다시 시도하기 전에 대기할 시간. 일부 장애 시나리오에서 요청이 반복적으로 타이트하게 전송되는 것을 방지할 수 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[책 정리] 빅데이터를 지탱하는 기술 #Ch2-1]]></title>
            <link>https://velog.io/@suhyun-guri/%EC%B1%85-%EC%A0%95%EB%A6%AC-%EB%B9%85%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EC%A7%80%ED%83%B1%ED%95%98%EB%8A%94-%EA%B8%B0%EC%88%A0-Ch2-1</link>
            <guid>https://velog.io/@suhyun-guri/%EC%B1%85-%EC%A0%95%EB%A6%AC-%EB%B9%85%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EC%A7%80%ED%83%B1%ED%95%98%EB%8A%94-%EA%B8%B0%EC%88%A0-Ch2-1</guid>
            <pubDate>Mon, 02 May 2022 05:34:50 GMT</pubDate>
            <description><![CDATA[<p><span style='color:gray'> <em>본 내용은 <code>빅데이터를 지탱하는 기술 (니시다 케이스케)</code> 책을 정리한 내용입니다.</em> <span>
<span style='color:gray'> <em>Chapter 2 빅데이터의 탐색 2-1 ~ 2-2</em> <span> <br>
<img src=https://velog.velcdn.com/images/suhyun-guri/post/09cb1adf-fd87-4be0-bbd1-7ecba34241a3/image.jpg width=350></p>
<hr />

<h1 id="2-1-크로스-집계의-기본">2-1 크로스 집계의 기본</h1>
<h2 id="데이터-집계---데이터-마트---시각화">데이터 집계 -&gt; 데이터 마트 -&gt; 시각화</h2>
<p>데이터의 집계와 시각화 사이에 있는 것이 데이터 마트다. </p>
<ul>
<li>데이터 마트가 작을수록<ul>
<li>시각화하는 것이 간단해짐. </li>
<li>원래 데이터에 포함된 정보를 잃어버리게 되어 시각화 프로세스에서 할 수 있는 일이 적어짐.</li>
<li>피벗 테이블과 BI 도구를 사용해 대화적인 데이터 검색한다면, 정보 부족으로 곤란한 상황 발생</li>
</ul>
</li>
<li>데이터 집계의 프로세스에서 많은 정보를 남기게 되면<ul>
<li>데이터 마트가 거대화되어 좋은 시각화를 할 수 없음</li>
</ul>
</li>
</ul>
<p>이는 Trade off의 관계에 있으며, 필요에 따라 어느 정도의 정보를 남길 것인가를 결정해야 한다.
즉, <strong>데이터 마트의 크기</strong>에 따라 시스템 구성이 결정된다.</p>
<h1 id="2-2-열-지향-스토리지에-의한-고속화">2-2 열 지향 스토리지에 의한 고속화</h1>
<p>메모리에 다 올라가지 않을 정도의 대량의 데이터를 신속하게 집계하려면, 미리 데이터를 집계에 적합한 형태로 변환하는 것이 필요하다.</p>
<h2 id="데이터베이스의-지연을-줄이기">데이터베이스의 지연을 줄이기</h2>
<p>데이터양이 증가함에 따라 집계에 걸리는 시간은 길어진다. <br>
<strong>3계층의 데이터 집계 시스템</strong></p>
<ul>
<li>데이터 레이크 -&gt; 데이터 마트 -&gt; 시각화 도구</li>
</ul>
<ol>
<li>원 데이터는 용량적인 제약이 적어서 대량의 데이터를 처리할 수 있는 데이터 레이크와 데이터 웨어하우스에 저장한다. </li>
<li>거기에서 원하는 데이터를 추출하여 데이터 마트를 구축하고 여기에서는 항상 초 단위의 응답을 얻을 수 있도록 한다.</li>
</ol>
<h3 id="데이터-처리의-지연">데이터 처리의 지연</h3>
<p>데이터 처리의 응답이 빠르다는 것은 &#39;대기시간이 적다&#39;, &#39;지연이 적다&#39;라고 한다. <br>
데이터 마트를 만들 때는 가급적 지연이 적은 데이터베이스가 있어야 한다. 크게 두 가지 선택이 있다.</p>
<ol>
<li>모든 데이터를 메모리에 올리는 것<ul>
<li>만일 한 레코드 크기가 500byte라고 하면 천만 레코드는 5GB가 된다. 이 정도는 MySQL이나 PostgreSQL 등의 일반적인 RDB가 데이터 마트에 적합하다.</li>
<li>RDB는 원래 지연이 적고, 많은 수의 클라이언트가 동시 접속해도 성능이 나빠지지 않으므로 많은 사용자가 사용하는 실제 운영 환경의 데이터 마트로 특히 우수하다.</li>
<li>but RDB는 메모리가 부족하면 급격히 성능이 떨어진다.</li>
</ul>
</li>
<li>&#39;압축&#39;과 &#39;분산&#39;에 의해 지연 줄이기 - MPP 기술<ul>
<li>고속화를 위해 사용되는 기법이 &#39;압축&#39;과 &#39;분산&#39;이다. 데이터를 가능한 한 작게 압축하고 그것을 여러 디스크에 분산함으로써 데이터 로드에 따른 지연을 줄인다.</li>
<li>분산된 데이터를 읽어 들이려면 멀티 코어를 활용하면서 디스크 I/O를 병렬 처리하는 것이 효과적이다.</li>
<li>이러한 아키텍쳐를 MPP(massive parallel processing, 대규모 병렬 처리)라고 부르며 대량의 데이터를 분석하기 위해 데이터베이스에서 널리 사용되고 있다. (ex. Amazon Redshift, Google BigQuery)</li>
<li>MPP는 데이터 집계에 최적화되어 있으며, 데이터 웨어하우스와 데이터 분석용의 데이터베이스에서 특히 많이 사용된다. </li>
</ul>
</li>
</ol>
<h2 id="열-지향-데이터베이스-접근">열 지향 데이터베이스 접근</h2>
<h3 id="행-지향-데이터베이스">행 지향 데이터베이스</h3>
<p>빅데이터로 취급되는 데이터 대부분은 디스크 상에 있기 때문에 쿼리에 필요한 최소한의 데이터만을 가져옴으로써 지연이 줄어들게 된다. 이를 위해 사용되는 방법이 &#39;칼럼 단위로의 데이터 압축&#39;이다. 이를 &#39;행 지향 데이터베이스(row-oriented database)&#39;라고 부른다. <br>
 Oracle Database, MySQL 같은 일반적인 RDB는 행 지향 데이터베이스이다.</p>
<ul>
<li>행 지향 데이터베이스에서는 테이블의 각 행을 하나의 덩어리로 디스크에 저장한다.</li>
<li>새 레코드를 추가할 때, 파일의 끝에 데이터를 쓸 뿐이므로 빠르게 추가할 수 있다.</li>
<li>매일 발생하는 대량의 트랜잭션을 지연없이 처리하기 위해 데이터 추가를 효율적으로 하는 것이 특징이다.</li>
<li>데이터 검색을 고속화하기 위해 Index를 만든다.</li>
<li>but 데이터 분석에는 어떤 칼럼이 사용되는지 미리 알 수 없기에 Index가 도움이 되지 않는다. 필연적으로 대량의 데이터 분석은 항상 디스크 I/O를 동반한다. 따라서 인덱스에 의지하지 않는 고속화 기술이 필요하다.</li>
</ul>
<h3 id="열-지향-데이터베이스">열 지향 데이터베이스</h3>
<p>데이터 분석에 사용되는 데이터베이스는 칼럼 단위의 집계에 최적화되어 있으며, &#39;열 지향 데이터베이스(column-oriented databse)&#39; 또는 &#39;칼럼 지향 데이터베이스(columnar database)&#39;라고 한다. <br>
Teradata, Amazon Redshint 등이 열 지향 데이터베이스이다.</p>
<ul>
<li>데이터를 미리 칼럼 단위로 정리해둠으로써 필요한 칼럼만을 로드하여 데이터 I/O를 줄인다.</li>
<li>데이터의 압축 효율이 우수하다. 데이터의 종류에 따라 다르지만, 압축되지 않은 행 지향 데이터베이스와 비교하면 1/10 이하로 압축할 수 있다.</li>
</ul>
<h3 id="mpp-데이터베이스의-접근-방식">MPP 데이터베이스의 접근 방식</h3>
<p>쿼리 지연을 줄일 또 다른 방법은 MPP 아키텍쳐에 의한 데이터 처리의 병렬화다. <br></p>
<ul>
<li>행 지향 데이터베이스에서<ul>
<li>보통 하나의 쿼리는 하나의 스레드에서 실행된다. 많은 쿼리를 동시에 실행함으로써 여러 개의 CPU 코어를 활용할 수 있지만, 개별 쿼리가 분산 처리되는 것은 아니다.</li>
<li>각 쿼리는 짧은 시간에 끝나는 것으로 생각하므로, 하나의 쿼리를 분산 처리하는 상황은 가정하지 않는다.</li>
</ul>
</li>
<li>열 지향 데이터베이스에서<ul>
<li>디스크에서 대량의 데이터를 읽기 때문에 1번의 쿼리 실행 시간이 길어진다.</li>
<li>압축된 데이터의 전개 등으로 CPU 리소스를 필요로 하므로 멀티 코어를 활용하여 고속화하는 것이 좋다.</li>
</ul>
</li>
</ul>
<p>MPP에서는 하나의 쿼리를 다수의 작은 task로 분해하고 이를 가능한 한 병렬로 실행한다. </p>
<img src=https://velog.velcdn.com/images/suhyun-guri/post/8da89e20-a351-4c62-991e-f17ab861938d/image.jpg width=700px>

<h3 id="mpp-데이터베이스와-대화형-쿼리-엔진">MPP 데이터베이스와 대화형 쿼리 엔진</h3>
<p>쿼리가 잘 병렬화할 수 있다면, MPP를 사용한 데이터의 집계는 CPU 코어 수에 비례하여 고속화된다.
단, 디스크로부터 로드가 병목 현상이 발생하지 않도록 데이터가 고르게 분산되어야 한다.</p>
<p>하드웨어 수준에서 데이터 집계에 최적화된 데이터베이스를 <strong>MPP 데이터베이스</strong>라고 한다. <br/>
MPP의 아키텍처는 Hadoop과 함께 사용되는 대화형 쿼리 엔진으로도 채택되고 있다. 이 경우 데이터를 저장하는 것은 분산 스토리지의 역할이다. 그러나 데이터를 열 지향으로 압축하지 않는 한 MPP 데이터베이스와 동등한 성능은 되지 못한다. 그래서 Hadoop 상에서 열 지향 스토리지를 만들기 위해 여러 라이브러리가 개발되고 있다.</p>
<p>수억 레코드를 초과하는 데이터 마트의 지연을 작게 유지하기 위해서는 데이터를 열 지향의 스토리지 형식으로 저장해야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[책 정리] 빅데이터를 지탱하는 기술 #Ch1-2]]></title>
            <link>https://velog.io/@suhyun-guri/%EC%B1%85-%EC%A0%95%EB%A6%AC-%EB%B9%85%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EC%A7%80%ED%83%B1%ED%95%98%EB%8A%94-%EA%B8%B0%EC%88%A0-2</link>
            <guid>https://velog.io/@suhyun-guri/%EC%B1%85-%EC%A0%95%EB%A6%AC-%EB%B9%85%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EC%A7%80%ED%83%B1%ED%95%98%EB%8A%94-%EA%B8%B0%EC%88%A0-2</guid>
            <pubDate>Mon, 18 Apr 2022 01:09:39 GMT</pubDate>
            <description><![CDATA[<p><span style='color:gray'> <em>본 내용은 <code>빅데이터를 지탱하는 기술 (니시다 케이스케)</code> 책을 정리한 내용입니다.</em> <span>
<span style='color:gray'> <em>Chapter 1 빅데이터의 기초 지식 1-1 ~ 1-2</em> <span>
<img src=https://velog.velcdn.com/images/suhyun-guri/post/09cb1adf-fd87-4be0-bbd1-7ecba34241a3/image.jpg width=350></p>
<hr />

<h2 id="1-3-스크립트-언어-활용">1-3. 스크립트 언어 활용</h2>
<p>데이터 분석 분야에서 자주 사용되는 스크립트 언어에는  R과 Python이다. </p>
<p><strong>Python 특징</strong></p>
<ul>
<li>파이썬은 범용의 스크립트 언어로 발전한 역사가 있고, 다양한 분야의 라이브러리를 사용할 수 있다.</li>
<li>특히 외부 시스템의 API를 호출하거나, 복잡한 문자열 처리가 필요한 데이터 전처리에 적합하다.</li>
<li>Numpy와 Scipy라는 수치 계산용 라이브러리와 머신러닝 프레임워크가 충실하다.</li>
<li>데이터 처리 분야에서는 R에서 사용하는 데이터 프레임의 모델을 파이썬으로 만든 라이브러리인 Pandas를 많이 사용하고 있다.</li>
</ul>
<h3 id="데이터-프레임">데이터 프레임</h3>
<p>데이터 프레임은 표 형식의 데이터를 추상화한 객체</p>
<p>데이터 프레임을 사용하면 스크립트 언어 안에서 데이터 가공과 집계를 할 수 있다.</p>
<p><strong>데이터 전처리에 사용할 수 있는 pandas의 함수</strong></p>
<table>
<thead>
<tr>
<th>이름</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>ix</td>
<td>조건에 일치하는 데이터 검색</td>
</tr>
<tr>
<td>drop</td>
<td>지정한 행(칼럼) 삭제</td>
</tr>
<tr>
<td>rename</td>
<td>인덱스 값(칼럼명) 변경</td>
</tr>
<tr>
<td>dropna</td>
<td>값이 없는 행(칼럼명) 제외</td>
</tr>
<tr>
<td>fillna</td>
<td>값이 없는 셀을 지정한 값으로 치환</td>
</tr>
<tr>
<td>apply</td>
<td>각 칼럼(행)에 함수 적용</td>
</tr>
</tbody></table>
<p>pandas에는 시계열 데이터를 취급하기 위한 다양한 기능이 있다. 시간을 인덱스로 지정함으로써 시계열 데이터를 분석할 수 있다.</p>
<p><strong>SQL 결과를 데이터 프레임으로</strong></p>
<pre><code class="language-python">import pandas as pd
import sqlalchemy
#데이터베이스에 접속
engine = sqlalchemy.create_engine(&#39;sqlite:///sample.db&#39;)

#쿼리를 실행해서 데이터 프레임으로 변환
query = &#39;&#39;&#39;
SELECT substr(time, 1, 10) time, count(*) count
FROM   access_log
WHERE  time BETWEEN &#39;1995-07-01&#39; AND &#39;1995-07-04&#39;
GROUP BY 1 ORDER BY 1
&#39;&#39;&#39;

pd.read_sql(query, engine)</code></pre>
<h2 id="1-4-bi-도구와-모니터링">1-4. BI 도구와 모니터링</h2>
<p><strong><em>데이터 탐색에서 중요한 것은 우선 큰 그림을 파악한 후에 점차 세부 사항으로 깊이 들어가는 것.</em></strong> </p>
<h3 id="모니터링">모니터링</h3>
<p>모니터링 : 계획적으로 데이터의 변화를 추적해 나가는 것</p>
<p>데이터는 현재 상황을 파악하기 위한 하나의 도구로 사용할 수 있다. 비정상적인 상태를 나타내는 징후가 있다면, 무언가 조치를 취해야 한다. 즉, <strong>자신의 다음 행동을 결정하기 위한 재료로서 데이터를 살펴본다</strong>는 것이다.</p>
<h3 id="데이터에-근거한-의사-결정">데이터에 근거한 의사 결정</h3>
<p>KPI(Key Peroformance Indicator) : 프로젝트의 현황을 파악하기 위한 숫자</p>
<p>ex) <strong>웹 서비스의 KPI</strong></p>
<table>
<thead>
<tr>
<th>약칭</th>
<th>정식 명칭</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td>DAU</td>
<td>Daily Active User</td>
<td>서비스를 이용한 1일 유저 수</td>
</tr>
<tr>
<td>계속률</td>
<td>Customer Retention</td>
<td>서비스를 계속해서 이용하고 있는 유저의 비율</td>
</tr>
<tr>
<td>ARPPU</td>
<td>Average Revenue Per Paid User</td>
<td>유료 고객 1인당 평균 매출</td>
</tr>
</tbody></table>
<p>KPI 모니터링에서 의식하고 싶은 것은 그것이 <strong>행동 가능(actionable)한 것인가</strong> 이다. 즉, 결과에 따라 자신의 다음 행동이 결정될 지의 여부다. 행동 가능한 숫자를 만들기 위해서는 그것이 좋은지 나쁜지 판단하기 위한 판단 기준이 필요하다.</p>
<p>자신의 행동을 결정할 때 객관적인 데이터를 근거하여 판단하는 것을 <strong>‘데이터 기반(data-driven) 의사 결정’</strong>이라고 한다.</p>
<h3 id="변화를-파악하고-세부-사항을-이해하기">변화를 파악하고 세부 사항을 이해하기</h3>
<p>무료로 사용할 수 있는 BI 도구 예</p>
<table>
<thead>
<tr>
<th>명칭</th>
<th>종류</th>
</tr>
</thead>
<tbody><tr>
<td>Tableau Public</td>
<td>데스크톱 + 웹 서비스</td>
</tr>
<tr>
<td>Quick Sencse</td>
<td>데스크톱 + 웹 서비스</td>
</tr>
<tr>
<td>Microsoft Power BI</td>
<td>데스크톱 + 웹 서비스</td>
</tr>
<tr>
<td>구글 Data Studio</td>
<td>웹 서비스</td>
</tr>
</tbody></table>
<p>Tableau Public은 주로 블로그 등에서 공개하는 데이터를 위해 만들어져 있으므로 회사 내의 데이터를 분석하는 데는 적합하지 않다.</p>
<p>데이터의 움직임을 모니터링하기 위한 기본적인 전략은 우선 정기적인 보고를 통해 중요한 변화를 파악하는 것이다. 그리고 그 원인을 알기 위해 원인이 되는 데이터로 돌아와 재집계를 반복하며 자세히 살펴보는 것이다.</p>
<p>BI 도구는 이를 위한 소프트웨어이고 데이터를 자세히 탐색할 때 그 힘을 발휘한다. BI 도구는 자신이 직접 데이터를 보기 위한 소프트웨어이며, 집계의 단면을 다양하게 전환하면서 원하는 정보를 찾아낼 수 있다.</p>
<p>문제는 항상 이상적인 데이터가 존재할 수 없다는 것이다. 원하는 대로 집계 결과를 얻으려면 ‘시각화하기 쉬운 데이터’를 만들어야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[책 정리] 빅데이터를 지탱하는 기술 #Ch1-1]]></title>
            <link>https://velog.io/@suhyun-guri/%EC%B1%85-%EC%A0%95%EB%A6%AC-%EB%B9%85%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EC%A7%80%ED%83%B1%ED%95%98%EB%8A%94-%EA%B8%B0%EC%88%A0-1</link>
            <guid>https://velog.io/@suhyun-guri/%EC%B1%85-%EC%A0%95%EB%A6%AC-%EB%B9%85%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EC%A7%80%ED%83%B1%ED%95%98%EB%8A%94-%EA%B8%B0%EC%88%A0-1</guid>
            <pubDate>Sat, 16 Apr 2022 08:45:41 GMT</pubDate>
            <description><![CDATA[<p><span style='color:gray'> <em>본 내용은 <code>빅데이터를 지탱하는 기술 (니시다 케이스케)</code> 책을 정리한 내용입니다.</em> <span>
<span style='color:gray'> <em>Chapter 1 빅데이터의 기초 지식 1-1 ~ 1-2</em> <span>
<img src=https://velog.velcdn.com/images/suhyun-guri/post/09cb1adf-fd87-4be0-bbd1-7ecba34241a3/image.jpg width=350></p>
<hr />

<h1 id="1-1-빅데이터의-정착">1-1 빅데이터의 정착</h1>
<p>“빅데이터”라는 단어를 자주 접하게 된 것은 2011년 후반에서 2012년에 걸쳐 많은 기업들이 데이터 처리에 분산 시스템을 도입하기 시작했을 무렵이다. </p>
<p>빅데이터의 취급이 어려운 이유는 크게 두 가지이다.</p>
<ol>
<li>데이터의 분석 방법을 모른다</li>
<li>데이터 처리에 수고와 시간이 걸린다.</li>
</ol>
<p>그러나 이 두 가지를 갖추고 나서야 비로소 가치 있는 정보를 얻을 수 있다.</p>
<h3 id="빅데이터-기술의-요구---hadoop-nosql">빅데이터 기술의 요구 - Hadoop, NoSQL</h3>
<p>빅데이터의 기술로 가장 먼저 예로 들 수 있는 것이 Hadoop과 NoSQL이다.</p>
<p>웹 서버 등에서 생성된 데이터는 처음에는 RDB와 NoSQL 등의 텍스트 데이터에 저장된다. 그 후 모든 데이터가 Hadoop으로 모이고, 거기서 대규모 데이터 처리가 실행된다.</p>
<p>데이터가 많아지면서 전통적인 관계형 데이터베이스(RDB)로는 취급할 수 없을 만큼 쌓이게 되었다. 그래서 Hadoop과 NoSQL이 각각 다른 요구를 충족시키기 위해 나타났다.</p>
<blockquote>
<p>NoSQL이란?</p>
</blockquote>
<ul>
<li><strong>No SQL, Not Only SQL</strong><ul>
<li>단순히 기존 관계형 DBMS가 갖고 있는 특성뿐만 아니라 다른 특성들을 부가적으로 지원한다는 것을 의미</li>
<li>기존의 관계형 데이터베이스보다 더 융통성있는 데이터 모델을 사용하고 데이터의 저장 및 검색을 위한 특화된 매커니즘을 제공한다. 단순 검색 및 추가 작업에 있어서 매우 최적화된 키-값 저장 기법을 사용하여 응답속도나 처리 효율 등에 있어서 뛰어난 성능을 나타낸다.</li>
<li>즉, NoSQL은 초고용량 데이터 처리 등 성능에 특화된 목적을 위해 비관계형 데이터 저장소에 비구조적인 데이터를 저장하기 위한 분산 저장 시스템이다.
<a href="https://www.samsungsds.com/kr/insights/1232564_4627.html">[참고자료] Samsung SDS - NoSQL이란 무엇인가?</a></li>
</ul>
</li>
</ul>
<p><strong>Hadoop</strong></p>
<ul>
<li>Hadoop은 다수의 컴퓨터에서 대량의 데이터를 처리하기 위한 시스템이다.</li>
<li>방대한 데이터를 저장해둘 스토리지와 데이터를 순차적으로 처리할 수 있는 구조가 필요하다. 이를 위해서는 수백 수천 대의 컴퓨터가 이용되어야 하며 이를 관리하는 것이 Hadoop이다.</li>
<li>구글에서 개발된 분산 처리 프레임워크인 MapReduce를 참고하여 제작되었다.</li>
<li>SQL과 같은 쿼리 언어를 Hadoop에서 실행하기 위한 소프트웨어로 Hive가 개발되었다. Hive의 도입으로 프로그래밍 없이 데이터를 집계할 수 있게 함으로써 많은 사람이 사용할 수 있게 되었다.</li>
</ul>
<p><strong>NoSQL 데이터베이스</strong></p>
<ul>
<li>NoSQL은 전통적인 RDB의 제약을 제거하는 것을 목표로 한 데이터베이스의 총칭이다.</li>
<li>다양한 종류가 있다. 다수의 키와 값을 관련지어 저장하는 <code>key-value store</code>, JSON과 같은 복잡한 데이터 구조를 저장하는 <code>document store</code>, 여러 키를 사용하여 높은 확장성을 제공하는 <code>wide-column store</code> 등이 대표적이다.</li>
<li>RDB보다 고속의 읽기, 쓰기가 가능하고 분산 처리에 뛰어나다는 특징을 갖추고 있다.</li>
<li>모여진 데이터를 나중에 집계하는 것이 목적인 Hadoop과 다르게 NoSQL은 애플리케이션에서 온라인으로 접속하는 데이터베이스이다.</li>
</ul>
<p><strong>Hadoop + NoSQL 조합</strong></p>
<ul>
<li>NoSQL 데이터베이스에 기록하고 Hadoop으로 분산 처리하기</li>
<li><strong>방대한 규모로 계속 증가하는 데이터에 대해 현실적인 비용으로 데이터를 처리</strong>할 수 있게 되었다.</li>
</ul>
<h1 id="1-2-빅데이터-시대의-데이터-분석-기반">1-2 빅데이터 시대의 데이터 분석 기반</h1>
<p>빅데이터 기술이 기존의 데이터 웨어하우스와 다른 점은 다수의 분산 시스템을 조합하여 확장성이 뛰어난 데이터 처리 구조를 만든다는 점이다. 이 책에서 다루는 ‘빅데이터 기술’이란 분산 시스템을 활용하면서 데이터를 순차적으로 가공해나가는 일련의 구조다.</p>
<h3 id="데이터-파이프라인">데이터 파이프라인</h3>
<p>데이터 파이프라인은 일반적으로 차례대로 전달해나가는 데이터로 구성된 시스템을 말한다.</p>
<p>빅데이터의 데이터 파이프라인은 어디에서 데이터를 수집하여 무엇을 실현하고 싶은 지에 따라 변화한다. 처음에는 간단한 구성으로도 끝나지만, 하고 싶은 일이 증가함에 따라 시스템은 점차 복잡해지고 그것을 어떻게 조합시킬지가 문제가 된다.</p>
<h3 id="데이터-수집">데이터 수집</h3>
<p>데이터 파이프라인은 데이터 수집부터 시작한다. 데이터는 여러 장소에서 발생하고 각각 다른 형태를 보인다. 또한 서로 다른 기술로 데이터를 전송한다.</p>
<p>데이터 전송(data transfer)의 방법은 크게 두 가지가 있다.</p>
<ol>
<li>벌크(bulk) 형</li>
<li>스트리밍(streaming) 형</li>
</ol>
<p><img src="https://velog.velcdn.com/images/suhyun-guri/post/4497a4dc-0517-41b1-aea9-98ba7572bc26/image.jpg" alt="빅데이터를 위한 데이터 파이프라인"></p>
<p>벌크 형은 이미 어딘가에 존재하는 데이터를 정리해 추출하는 방법으로, 데이터베이스와 파일 서버 등에서 정기적으로 데이터를 수집하는 데에 사용한다. </p>
<p>스트리밍 형은 차례차례로 생성되는 데이터를 끊임없이 계속해서 보내는 방법으로 모바일 애플리케이션과 임베디드 장비 등에서 널리 데이터를 수집하는 데 사용된다.</p>
<h3 id="스트림-처리와-배치-처리">스트림 처리와 배치 처리</h3>
<ul>
<li><strong>스트림 처리(stream processing)</strong> : 모바일 애플리케이션 등에서 데이터를 실시간으로 처리하는 것, 장기적인 데이터 분석에는 적합하지 않은 문제</li>
<li><strong>배치 처리(batch processing)</strong> : 어느 정도 정리된 데이터를 효율적으로 가공하는 것, (장기적인 데이터 분석을 목적으로) 대량의 데이터를 저장하고 처리하는 데 적합</li>
</ul>
<h3 id="분산-스토리지">분산 스토리지</h3>
<p>수집된 데이터는 분산 스토리지(distribute storage)’에 저장된다.
분산 스토리지 : 여러 컴퓨터와 디스크로부터 구성된 스토리지 시스템</p>
<p>데이터를 저장하는 방법 </p>
<ol>
<li>객체 스토리지 : 한 덩어리로 모인 데이터에 이름을 부여해서 파일로 저장, 클라우드 서비스인 Amazon S3 등이 유명</li>
<li>NoSQL 데이터베이스 : 애플리케이션에서 많은 데이터를 읽고 쓰는 데에 있어서 성능이 우수, 단 나중에 데이터 용량을 얼마든지 늘릴 수 있는 확장성이 높은 제품을 선택해야 함</li>
</ol>
<h3 id="분산-데이터-처리">분산 데이터 처리</h3>
<p>분산 스토리지에 저장된 데이터를 처리하는 데는 분산 데이터 처리의 프레임워크가 필요하다.</p>
<p>MapReduce가 사용되어진 것이 바로 이 부분, 데이터양과 처리의 내용에 따라 많은 컴퓨터 자원이 필요하게 된다.</p>
<p>분산 데이터 처리의 주 역할은 나중에 분석하기 쉽도록 데이터를 가공해서 그 결과를 외부 데이터베이스에 저장하는 것</p>
<p>빅데이터를 SQL로 집계할 때 두 가지 방법</p>
<ol>
<li>쿼리 엔진을 도입 : Hive, 현재는 Hive보다도 고속인 대화형 쿼리 엔진도 개발되었다.</li>
<li>외부의 데이터 웨어하우스 제품을 이용 : 분산 스토리지에서 추출한 데이터를 데이터 웨어하우스에 적합한 형식으로 변환해야 한다. → ETL 프로세스 (데이터를 추출(extract), 가공(transform), 데이터 웨어하우스에 로드(load))</li>
</ol>
<h3 id="워크플로-관리">워크플로 관리</h3>
<p>전체 데이터 파이프라인의 동작을 관리하기 위해서 ‘워크플로 관리’ 기술을 사용한다.</p>
<p>매일 정해진 시간에 배치 처리를 스케줄대로 실행하고, 오류가 발생한 경우에는 관리자에게 통지하는 목적으로 사용</p>
<p>데이터 파이프라인이 복잡해짐에 따라 그것을 한 곳에서 제어하지 않으면 전체의 움직임을 파악하기가 어렵다. 오류 발생 시의 처리와 다시 처리하기 위한 기능을 만드는 것을 빼놓아서는 안된다.</p>
<h3 id="데이터-웨어하우스와-데이터-마트">데이터 웨어하우스와 데이터 마트</h3>
<p><img src="https://velog.velcdn.com/images/suhyun-guri/post/f68a177e-0047-44e2-a962-597da0771d15/image.jpg" alt=""></p>
<p>데이터 웨어하우스는 대량의 데이터를 장기 보존하는 것에 최적화되어 있다. 정리된 데이터를 한 번에 전송하는 것은 뛰어나지만, 소량의 데이터를 자주 쓰고 읽는 데는 적합하지 않다.</p>
<p>전형적인 사용 방법 - 업무 시스템에서 꺼낸 데이터를 하루가 끝날 때 정리하여 쓰고, 이를 야간에 집계해서 보고서를 작성</p>
<ul>
<li>데이터 소스(data source) : 업무 시스템을 위한 RDB나 로그 등을 저장하는 파일 서버</li>
<li>ETL 프로세스 : 데이터 소스에 존재하는 raw 데이터를 추출하고 필요에 따라 가공한 후 데이터 웨어하우스에 저장하기까지의 흐름</li>
</ul>
<p>데이터 웨어하우스는 중요한 데이터 처리에 사용되기 때문에 함부로 사용해 시스템 과부하를 초래하면 안된다. 따라서 데이터 분석과 같은 목적에 사용하는 경우에는 데이터 웨어하우스에서 필요한 데이터만을 추출하여 <strong>데이터 마트(data mart)</strong>를 구축한다. 데이터 마트는 BI 도구와 조합시키는 형태로 데이터를 시각화하는 데에도 사용된다.</p>
<p>데이터 웨어하우스, 데이터 마트 모두 SQL로 데이터를 집계한다. </p>
<p>→ 먼저 테이블 설계를 제대로 정한 후에 데이터를 투입한다. 특히 BI 도구로 데이터를 볼 경우에는 미리 시각화에 적합한 형태로 테이블을 준비해야 한다.</p>
<h3 id="데이터-레이크">데이터 레이크</h3>
<p>: 모든 데이터를 원래의 형태로 축적해두고 나중에 그것을 필요에 따라 가공하기 위해 데이터를 축적해 놓는 곳</p>
<p><img src="https://velog.velcdn.com/images/suhyun-guri/post/f3674c99-87ee-427e-9177-202985d86cff/image.jpg" alt=""></p>
<p>구체적으로는 임의의 데이터를 저장할 수 있는 분산 스토리지가 데이터 레이크로 이용된다.</p>
<p>대부분의 경우는 CSV나 JSON 등 범용적인 텍스트 형식이 사용된다.</p>
<p><strong>데이터 레이크와 데이터 마트</strong></p>
<p>데이터 레이크는 단순한 스토리지므로 이것만으로 데이터를 가공할 수 없다. 그래서 사용되는 것이 MapReduce 등의 분산 데이터 처리 기술이다.</p>
<p>데이터 분석에 필요한 데이터를 가공, 집계하고, 이를 데이터 마트로 추출한 후에는 데이터 웨어하우스의 경우처럼 데이터 분석을 진행할 수 있다.</p>
<h3 id="데이터-엔지니어와-데이터-분석가">데이터 엔지니어와 데이터 분석가</h3>
<p><img src="https://velog.velcdn.com/images/suhyun-guri/post/e45b0626-f2db-4a4b-92b9-dc58f1aae004/image.jpg" alt=""></p>
<ul>
<li>데이터 엔지니어 : 시스템의 구축 및 운용, 자동화 등을 담당</li>
<li>데이터 분석가 : 데이터에서 가치 있는 정보를 추출</li>
</ul>
<h3 id="애드-혹-분석ad-hoc-analysis-및-대시보드-도구">애드 혹 분석(ad hoc analysis) 및 대시보드 도구</h3>
<ul>
<li>애드 혹 분석(ad hoc analysis) : 일회성 데이터 분석이라는 의미로 SQL 쿼리를 직접 작성해서 실행하거나 스프레드시트에서 그래프를 만드는 것까지 포함한 모든 수작업이 포함된다.</li>
</ul>
<p>애드 혹 분석에서는 데이터 마트를 만들지 않은 채 데이터 레이크와 데이터 웨어하우스에 직접 연결하는 경우가 많다. </p>
<p>수작업으로 정기적으로 그래프와 보고서를 만들고 싶을 때 도입하는 것이 대시보드 도구다.</p>
<p>대시보드 도구는 데이터 마트가 없어도 동작하도록 설계되어 있어 설정한 스케줄에 따라 데이터 레이크와 데이터 웨어하우스에 접속해 쿼리를 실행하고 그 결과로부터 그래프를 생성한다.</p>
<h3 id="데이터-마트와-워크플로-관리">데이터 마트와 워크플로 관리</h3>
<p>복잡한 데이터 분석에서는 먼저 데이터 마트를 구축한 후에 분석하거나 시각화하도록 한다.</p>
<p>시각화에 BI 도구를 사용할 경우는 집계 속도를 높이기 위해 데이터 마트가 거의 필수적이다. 데이터 마트 구축은 배치 처리로 자동화되는 경우가 많기 때문에 그 실행 관리를 위해 워크플로 관리 도구를 사용한다.</p>
<p>데이터 처리를 자동화해서 장기적으로 운용해 나가기 위해서는 안정된 워크플로 관리가 필수적이다.</p>
<h3 id="데이터를-수집하는-목적">데이터를 수집하는 목적</h3>
<p><strong>데이터 검색</strong></p>
<ul>
<li>대량의 데이터 중에서 조건에 맞는 것을 찾고 싶은 경우</li>
<li>언제 무엇이 필요할지조차도 모르기 때문에, 시스템 로그 및 고객의 행동 이력 등 발생하는 모든 데이터를 취득해 놓도록 한다.</li>
<li>필요할 때 신속하게 검색할 수 있어야 하므로 시스템에는 실시간 데이터 처리나 검색 엔진을 사용하여 키워드를 찾는 기능이 필요하다.</li>
</ul>
<p><strong>데이터 가공</strong></p>
<ul>
<li>업무 시스템의 일부로서 데이터 처리 결과를 이용하고 싶은 경우</li>
<li>이 경우 목적이 명확하기 때문에 필요한 데이터를 계획적으로 모아 데이터 파이프라인을 설계한다.</li>
<li>데이터 가공에는 자동화가 필수적이다. 워크플로 관리를 도입하여 꼼꼼하게 테스트를 반복적으로 실행해서 시스템을 구축한다. 시스템 개발 영역에 해당된다.</li>
</ul>
<p><strong>데이터 시각화</strong></p>
<ul>
<li>데이터를 시각적으로 봄으로써 앞으로의 상황을 예측해 의사 결정에 도움이 되도록 하는 경우</li>
<li>데이터 시각화는 시행착오의 연속이며, 확실한 해답은 없다.</li>
<li>임의의 분석 환경을 갖추고 여러 번 데이터 집계를 반복한다. 고속화를 위해 데이터 마트도 필요하다.</li>
<li>또한 집계 결과를 대시보드에 정리해서 계속 변화를 감시하고 싶을 때도 데이터 시각화는 필요하다.</li>
</ul>
<h3 id="확증적-데이터-분석과-탐색적-데이터-분석">확증적 데이터 분석과 탐색적 데이터 분석</h3>
<ul>
<li><p>확증적 데이터 분석(confirmatory data analysis) : 가설을 세우고 그것을 검증하는 것, 통계학적 모델링에 의한 데이터 분석</p>
</li>
<li><p>탐색적 데이터 분석(exploratory data analysis) : 데이터를 보면서 그 의미를 읽어내려고 하는 것, 데이터를 시각화하여 사람의 힘에 의한 데이터 분석</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[ResNet : Deep Residual Learning for Image Recognition]]></title>
            <link>https://velog.io/@suhyun-guri/ResNet-Deep-Residual-Learning-for-Image-Recognition</link>
            <guid>https://velog.io/@suhyun-guri/ResNet-Deep-Residual-Learning-for-Image-Recognition</guid>
            <pubDate>Wed, 09 Mar 2022 12:46:05 GMT</pubDate>
            <description><![CDATA[<h1 id="deep-residual-learning-for-image-recognition">Deep Residual Learning for Image Recognition</h1>
<hr>
<h2 id="1-introduction">1. Introduction.</h2>
<p>Deep networks는 일반적으로 low/mid/high 레벨의 피쳐들이 적절하게 추출되고 그러한 피쳐들의 레벨 또한 풍부해질 수 있다.</p>
<p><strong>_의문 :그렇다면 더 많은 층을 쌓으면 더 좋은 networks를 학습시킬 수 있는 것일까?
_</strong>
오래 전부터 단순히 층만 깊게 쌓는 것은 많은 문제를 야기할 수 있다고 알려져 왔다.</p>
<p>vanishing/exploding gradients 문제 → 가중치 값들을 초기에 적절히 초기화하는 것과 중간 정규화 계층에 의해 해결</p>
<p>본 논문은 층이 깊어짐에 따라 degradation 문제가 발생할 수 있다고 주장한다. 즉 층이 깊으면 accuracy가 무조건 높아지는 것이 아니라 어느 정도 이상 깊으면 오히려 accuracy가 감소할 수 있다는 것이다. 또한 이러한 문제는 단순히 overfitting로 야기되는 것이 아니며 층을 깊게 쌓으면 training error가 높아지는 문제가 생길 수 있다.</p>
<p><img src="https://images.velog.io/images/suhyun-guri/post/6b656d84-7e7f-4d08-ba81-0f88de745c09/Untitled%20(9).png" alt=""></p>
<p>위 그림은 CIFAR-10을 사용한 plain networks의 Training/Test error이다. 더 깊은 층인 56-layer의 error rate가 더 높은 것을 볼 수 있다. 학습 자체가 잘 안되는 것이다.</p>
<h2 id="2-deep-ressidual-learning">2. Deep ressidual learning.</h2>
<h3 id="residual-learning">Residual Learning</h3>
<p>본 논문에서는 degradation 문제를 해결하기 위해 <strong>deep residual learning framework</strong>를 제안한다. (Resnet)</p>
<p><img src="https://images.velog.io/images/suhyun-guri/post/f5dc7391-5666-43c8-b76f-da67d30a7b39/Untitled%20(10).png" alt=""></p>
<p>F(x)는 weight layer 두 개를 거친 이후의 값을 의미</p>
<p>기존의 기본 매핑을 $H(x)$라고 할 때(x는 layer의 input)이 $H(x)$는 여러 비선형 layer로 이루어져 천천히 복잡한 함수에 근사된다고 가정할 때,  $F(x) := H(x) - x$로 변형시켜 $F(x)+x$를 $H(x)$에 근사하도록 하는 것(Residual mapping)이 더 쉽다고 가정한다. 이를 feed-forward neural network에 적용한 것이 <strong>Shortcut connection</strong>이라고 한다. skip connection이라고도 한다.</p>
<blockquote>
<p>identity mapping : 입력으로 들어간 값 x 가 어떠한 함수를 통과하더라도 다시 x 가 나오는 것</p>
</blockquote>
<p>극단적으로 identity mapping이 최적의 해라고 했을 때, 함수 F가 0이 될 수 있게 하는 것이 학습 난이도가 더 쉽다. 다시말해 H가 x인 경우 즉 우리가 본질적으로 학습시키고자 하는 mapping이 identity mapping일 때, residual 자체가 0이 되도록 학습시키는 것이 더 쉽다.</p>
<p>shortcut connection은 단순히 identity mapping으로 사용할 수 있으며 출력값에 단순히 x를 더해주는 것이기 때문에 추가적인 파라미터가 필요하지도 않고 복잡도가 증가하지도 않으며 구현도 간단한 것이 장점이다.</p>
<p>본 논문은 1) residual network를 이용했을 때 학습 난이도가 더 쉽다. 2) residual network는 깊이가 깊어질수록 높은 accuracy를 보인다. 고 말한다.</p>
<p>또한 CIFAR-10, ImageNet에서 모두 성능이 좋아졌으며 특정 데이터셋에 국한된 방법이 아니다라고 한다.</p>
<h3 id="identity-mapping-by-shortcuts">Identity Mapping by Shortcuts.</h3>
<p>하나의 residual block을 다음과 같이 정의한다.</p>
<blockquote>
<p>$y = F(x, {W_i}) + x$</p>
</blockquote>
<p>$F(x, {W_i})$는 residual mapping을 의미하고 $x$는 identity mapping(즉, shortcut connection)을 의미한다.</p>
<p>앞서 Fig 2는 $F = W_2\sigma(W_1x)$ 이렇게 두 개의 weights를 중첩해서 사용하는 것이다. 여기서 $\sigma$는 ReLU이고 biases는 생략되었다.</p>
<p>만약 input($x$) dimension과 ouput($F$) dimension이 다르다고 할 때,</p>
<p>$y = F(x, {W_i}) + W_sx$ 이렇게 linear projection인 $W_s$를 곱해줌으로써 dimension을 맞춰준다.</p>
<p>또한 중첩된 layer가 아닌 Single layer의 경우 단순히 Linear layer이므로 ($y = W_1x + x$) 사용시 별다른 이점이 없다고 말하고 있다.</p>
<h3 id="plain-network">Plain Network.</h3>
<p>비교 목적으로 기본적인 CNN 모델을 가져와서 실험을 진행한다.</p>
<p>본 논문의 Plain network는 VGG Net에서 제안되었던 기법들을 적절히 따르고 있다고 한다.</p>
<p>layer마다 time complexity를 보존할 수 있는 형태로 네트워크를 구성, 별도의 pooling layer를 사용하지 않고 convolution layer에 stride를 2로 줌으로써 downsampling을 진행했다고 한다. </p>
<p>결과적으로 본 모델은 VGG Net보다 더 적은 파라미터를 사용하고 복잡도 또한 낮았다고 한다. 
<img src=https://images.velog.io/images/suhyun-guri/post/e0f78b31-cb8f-4516-a452-9170c1215282/Screenshot_20220309-164123_Samsung%20Notes.jpg width=400>
<span style="color:grey"> <em>점선은 input단과 output단의 dimension이 일치하지 않아서 맞춰주기 위한 테크닉이 가미된 부분이다.</em></span></p>
<p>VGG와 비교했을 때 FLOPs가 더 감소했다.</p>
<blockquote>
<p>FLOPs : 딥러닝 모델에서 계산 복잡도를 나타내기 위한 척도</p>
</blockquote>
<h3 id="residual-network">Residual Network.</h3>
<p>입력단과 출력단의 dimension이 같을 때 바로 identity mapping을 사용할 수 있다.</p>
<p>입력단과 출력단의 dimension이 다를 때는</p>
<ol>
<li>사이드에 padding을 해서 identity mapping을 수행</li>
<li>projection 연산을 사용해서 구현</li>
</ol>
<h3 id="implementation">Implementation</h3>
<p>실제 구현 상의 테크닉 설명 - ImageNet을 위해 사용</p>
<p>224X224로 랜덤하게 crop, horizontal flip 사용 가능</p>
<p>각 Convolution layer를 거칠 때마다 Batch Normalization 적용</p>
<p>learning rate는 0.1에서 시작해 학습이 진행되면서 점진적으로 줄어들 수 있도록 함</p>
<p>weight decay = 0.0001, momentum = 0.9</p>
<h2 id="3-experiments">3. Experiments</h2>
<h3 id="imagenet-2012-classification-dataset">ImageNet 2012 classification dataset</h3>
<p>training images 1.28 백만개, validation images는 5만장, test images는 10만장 사용</p>
<p><img src="https://images.velog.io/images/suhyun-guri/post/24ff1042-a658-4aec-903c-58247c17602f/Untitled%20(11).png" alt=""></p>
<p>Plain network의 경우 layer가 깊을수록 성능이 떨어지고 ResNet의 경우 layer가 깊을수록 성능이 높아지는 것을 확인할 수 있다.</p>
<p>본 연구에서는 이 문제가 vanishing gradients 때문에 발생한 문제가 아니라고 말하고 있다. 이 문제는 수렴률이 기하급수적으로 낮아지는 것이 문제라고 말하고 있다.</p>
<blockquote>
<p>convergence rates : 최적화 기법에서 등장하는 개념으로, 수렴을 위해 필요한 epoch이나 수렴 난이도를 언급하고자 할 때 사용하는 척도이다.</p>
</blockquote>
<p>결론적으로 Plain network와 비교했을 때 ResNet의 경우 더 깊은 layer가 얕은 layer에 비해서 잘 동작하고 있고 training rate도 낮고 일반화 성능 또한 높다고 한다.</p>
<p>수렴 속도 또한 더 빠르다는 것을 확인할 수 있다. ResNet 초기 단계에서 더 빠르게 수렴할 수 있도록 만들어줌으로써 optimization 자체를 더욱 쉽게 만들어주는 것이 장점이다.
<img src=https://images.velog.io/images/suhyun-guri/post/2c5278b3-c364-4e56-a1b6-9844df7caf1a/Untitled%20(12).png width=600>
<span style="color:grey"> <em>오른쪽이 Bottleneck</em></span></p>
<p>또한 본 논문에서 추가적으로 shortcut connection을 위해서 identity mapping과 projection mapping 사용시 결과를 실험을 통해 알려주고 있다.</p>
<p>3가지 방법이 있다.</p>
<pre><code>A) zero padding으로 dimension 늘려서 사용

B) dimension이 증가할 때만 projection 연산 수행

C) 모든 shortcut에 projection 적용</code></pre><p>실험 결과, C가 가장 성능이 좋았지만 projection shortcut이 필수적이라고 할 만큼 높은 개선은 아니라고 말하고 있다.</p>
<p>기본적으로 identity shortcut을 이용해서 성능을 많이 개선할 수 있으며 identity shortcut은 파라미터 자체가 없으므로 the bottleneck architectures에 대해서는 파라미터 수를 줄이는 데에 기여할 수 있으며 복잡도를 늘리지 않는 데에 효과적이다.</p>
<blockquote>
<p>bottleneck architectures
  사용 목적 : 연산량을 줄이기 위함, 파라미터 수를 줄이기 위함.
  참고 : <a href="https://coding-yoon.tistory.com/116">[딥러닝] DeepLearning CNN BottleNeck 원리(Pytorch 구현)</a></p>
</blockquote>
<ul>
<li>Standard는 Channel 수가 적을지라도, 3x3 Convolution을 두 번 통과했고,
BottleNeck은 1x1, 3x3, 1x1 순으로 Convolution을 통과하고, Channel 수는 4배 정도 많지만, Parameter가 세 배 정도 적다.</li>
</ul>
<h3 id="cifar-10-dataset">CIFAR-10 dataset</h3>
<p>32X32 images로 ImageNet보다 훨씬 작다. 그래서 CIFAR-10에 맞춰서 파라미터 수를 줄여서 별도의 ResNet을 고안해서 사용했다고 한다.</p>
<p>즉, ImageNet과 비교했을 때 구조가 다르긴 하지만 유사한 형태를 가지고 있다.</p>
<p><img src="https://images.velog.io/images/suhyun-guri/post/1f9b5004-c354-4516-ab37-9350db9124cd/Untitled%20(13).png" alt=""></p>
<p>결과를 보면 파라미터 수는 더 적지만 성능이 가장 좋은 것을 볼 수 있다.</p>
<h3 id="references">References</h3>
<ul>
<li><a href="https://arxiv.org/abs/1512.03385">Deep Residual Learning for Image Recognition</a></li>
<li><a href="https://youtu.be/671BsKl8d0E">ResNet: Deep Residual Learning for Image Recognition (꼼꼼한 딥러닝 논문 리뷰와 코드 실습)</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BaekJoon/Python] 문자열 : 11654, 11720, 10809, 2675, 1157, 1152, 2908, 5622, 2941, 1316]]></title>
            <link>https://velog.io/@suhyun-guri/BaekJoonPython-%EB%AC%B8%EC%9E%90%EC%97%B4-11654-11720-10809-2675-1157-1152-2908-5622-2941-1316</link>
            <guid>https://velog.io/@suhyun-guri/BaekJoonPython-%EB%AC%B8%EC%9E%90%EC%97%B4-11654-11720-10809-2675-1157-1152-2908-5622-2941-1316</guid>
            <pubDate>Thu, 03 Feb 2022 09:53:46 GMT</pubDate>
            <description><![CDATA[<h1 id="백준-단계-7-문자열">백준 단계 7, 문자열</h1>
<h2 id="11654-아스키-코드">#11654 아스키 코드</h2>
<blockquote>
<p>알파벳 소문자, 대문자, 숫자 0-9중 하나가 주어졌을 때, 주어진 글자의 아스키 코드값을 출력하는 프로그램을 작성하시오.</p>
</blockquote>
<pre><code class="language-py">a = input()
print (ord(a))
#한 줄 코드
print(ord(input()))</code></pre>
<ul>
<li><p><code>ord()</code> : 문자의 아스키 코드값을 리턴</p>
</li>
<li><p><code>chr()</code> : 아스키 코드값 입력으로 받아 그 코드에 해당하는 문자를 출력</p>
</li>
</ul>
<h2 id="11720-숫자의-합">#11720 숫자의 합</h2>
<blockquote>
<p>N개의 숫자가 공백 없이 쓰여있다. 이 숫자를 모두 합해서 출력하는 프로그램을 작성하시오.</p>
</blockquote>
<pre><code class="language-py">#11720
n = input()
print(sum([int(i) for i in input()]))
# map 사용
print(sum(map(int, input())))
#for문 사용
total = 0
nums = input()
for i in range(n):
    total += int(nums)
print(total)</code></pre>
<h2 id="10809-알파벳-찾기">#10809 알파벳 찾기</h2>
<blockquote>
<p>알파벳 소문자로만 이루어진 단어 S가 주어진다. 각각의 알파벳에 대해서, 단어에 포함되어 있는 경우에는 처음 등장하는 위치를, 포함되어 있지 않은 경우에는 -1을 출력하는 프로그램을 작성하시오.</p>
</blockquote>
<pre><code class="language-py">a = list(&#39;abcdefghijklmnopqrstuvwxyz&#39;)
al = [-1] * len(a)
s = input()
for i in s:
    idx = a.index(i)
    if al[idx] == -1:
        al[idx] += s.index(i)+1
for k in al:
    print(k, end=&#39; &#39;)</code></pre>
<h4 id="다른-방식"><strong>다른 방식</strong></h4>
<ul>
<li>아스키코드에서 a= 97, z= 122</li>
<li>find 함수 <ul>
<li>find 함수를 이용해서 입력받은 문자열 안에 chr 함수로 변환된 문자가 있는지 찾는다. 만일 문자열이 있으면 찾는 문자가 첫 번째에 위치한 인덱스 숫자를 출력하고 없으면 -1을 출력하게 된다.</li>
<li>참고 : <a href="https://ooyoung.tistory.com/68">https://ooyoung.tistory.com/68</a><pre><code class="language-py">word = input()
alphabet = list(range(97,123))  # 아스키코드 숫자 범위
</code></pre>
</li>
</ul>
</li>
</ul>
<p>for x in alphabet :
    print(word.find(chr(x)), end=&#39; &#39;) </p>
<pre><code>
## #2675 문자열 반복
&gt; 문자열 S를 입력받은 후에, 각 문자를 R번 반복해 새 문자열 P를 만든 후 출력하는 프로그램을 작성하시오. 즉, 첫 번째 문자를 R번 반복하고, 두 번째 문자를 R번 반복하는 식으로 P를 만들면 된다. S에는 QR Code &quot;alphanumeric&quot; 문자만 들어있다.
QR Code &quot;alphanumeric&quot; 문자는 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\$%*+-./: 이다.

```py
for i in range(int(input())):
    a = input().split()
    print(&#39;&#39;.join([i* int(a[0]) for i in a[1]]))</code></pre><h2 id="1157-단어-공부">#1157 단어 공부</h2>
<blockquote>
<p>알파벳 대소문자로 된 단어가 주어지면, 이 단어에서 가장 많이 사용된 알파벳이 무엇인지 알아내는 프로그램을 작성하시오. 단, 대문자와 소문자를 구분하지 않는다.</p>
</blockquote>
<pre><code class="language-py">#1157 - 처음 생각한 코드
s = input()
dic = {}
for i in s.upper():
    if i in dic:
        dic[i] += 1
    else:
        dic[i] = 1
# print(dic)
result = []
for i, v in dic.items():
    if v == max(dic.values()):
        result.append(i)
if len(result) &gt; 1:
    print(&#39;?&#39;)
else:
    print(result[0])</code></pre>
<pre><code class="language-py">#리스트, 인덱스 사용하는 코드 -&gt; 이게 더 효율적
s = input().upper()
li = list(set(s)) #중복제거

result = []
for i in li:
    result.append(s.count(i))
if result.count(max(result)) &gt; 1:
    print(&#39;?&#39;)
else:
    print(li[result.index(max(result))])</code></pre>
<h2 id="1152-단어의-개수">#1152 단어의 개수</h2>
<blockquote>
<p>영어 대소문자와 공백으로 이루어진 문자열이 주어진다. 이 문자열에는 몇 개의 단어가 있을까? 이를 구하는 프로그램을 작성하시오. 단, 한 단어가 여러 번 등장하면 등장한 횟수만큼 모두 세어야 한다.</p>
</blockquote>
<pre><code class="language-py">s = input()
print(len(s.split()))

#or
print(len(input().split()))</code></pre>
<h2 id="2908-상수">#2908 상수</h2>
<blockquote>
<p>상근이의 동생 상수는 수학을 정말 못한다. 상수는 숫자를 읽는데 문제가 있다. 이렇게 수학을 못하는 상수를 위해서 상근이는 수의 크기를 비교하는 문제를 내주었다. 상근이는 세 자리 수 두 개를 칠판에 써주었다. 그 다음에 크기가 큰 수를 말해보라고 했다.
상수는 수를 다른 사람과 다르게 거꾸로 읽는다. 예를 들어, 734와 893을 칠판에 적었다면, 상수는 이 수를 437과 398로 읽는다. 따라서, 상수는 두 수중 큰 수인 437을 큰 수라고 말할 것이다.
두 수가 주어졌을 때, 상수의 대답을 출력하는 프로그램을 작성하시오.</p>
</blockquote>
<pre><code class="language-py"># - string으로 받아서 거꾸로 바꾸고 int로 지정하여 비교
a, b = input().split()
a, b = int(a[::-1]), int(b[::-1])
print(max(a, b))</code></pre>
<h2 id="5622-다이얼">#5622 다이얼</h2>
<blockquote>
<p>상근이의 할머니는 아래 그림과 같이 오래된 다이얼 전화기를 사용한다.
전화를 걸고 싶은 번호가 있다면, 숫자를 하나를 누른 다음에 금속 핀이 있는 곳 까지 시계방향으로 돌려야 한다. 숫자를 하나 누르면 다이얼이 처음 위치로 돌아가고, 다음 숫자를 누르려면 다이얼을 처음 위치에서 다시 돌려야 한다.
숫자 1을 걸려면 총 2초가 필요하다. 1보다 큰 수를 거는데 걸리는 시간은 이보다 더 걸리며, 한 칸 옆에 있는 숫자를 걸기 위해선 1초씩 더 걸린다.
상근이의 할머니는 전화 번호를 각 숫자에 해당하는 문자로 외운다. 즉, 어떤 단어를 걸 때, 각 알파벳에 해당하는 숫자를 걸면 된다. 예를 들어, UNUCIC는 868242와 같다.
할머니가 외운 단어가 주어졌을 때, 이 전화를 걸기 위해서 필요한 최소 시간을 구하는 프로그램을 작성하시오.</p>
</blockquote>
<pre><code class="language-py">aph = [&#39;ABC&#39;, &#39;DEF&#39;, &#39;GHI&#39;, &#39;JKL&#39;, &#39;MNO&#39;, &#39;PQRS&#39;, &#39;TUV&#39;, &#39;WXYZ&#39;]
string = input()
time = 0
for i in aph:
    for j in i:
        for s in string:
            if j == s:
                time += aph.index(i)+3
print(time)
</code></pre>
<p>각 다이얼의 알파벳리스트를 만들고 각 알파벳들을 쪼개서 input값과 맞는지 확인한다.
맞으면 그 알파벳 뭉치의 인덱스를 찾아 3을 더해준다.
1을 갈 때 2초가 걸리기 때문에 &#39;ABC&#39;를 갈 때는 3초가 걸린다.</p>
<h2 id="2941-크로아티아-알파벳">#2941 크로아티아 알파벳</h2>
<blockquote>
<p>예를 들어, ljes=njak은 크로아티아 알파벳 6개(lj, e, š, nj, a, k)로 이루어져 있다. 단어가 주어졌을 때, 몇 개의 크로아티아 알파벳으로 이루어져 있는지 출력한다.
dž는 무조건 하나의 알파벳으로 쓰이고, d와 ž가 분리된 것으로 보지 않는다. lj와 nj도 마찬가지이다. 위 목록에 없는 알파벳은 한 글자씩 센다.</p>
</blockquote>
<pre><code class="language-py">li = [&#39;c=&#39;,&#39;c-&#39;,&#39;dz=&#39;,&#39;d-&#39;,&#39;lj&#39;,&#39;nj&#39;,&#39;s=&#39;,&#39;z=&#39;]
s = input()
for i in li:
    s = s.replace(i, &#39;*&#39;)
print(len(s))</code></pre>
<p>이 문제는 블로그를 참고했다.. replace...
<a href="https://ooyoung.tistory.com/74">https://ooyoung.tistory.com/74</a></p>
<h2 id="1316-그룹-단어-체커">#1316 그룹 단어 체커</h2>
<blockquote>
<p>그룹 단어란 단어에 존재하는 모든 문자에 대해서, 각 문자가 연속해서 나타나는 경우만을 말한다.
예를 들면, ccazzzzbb는 c, a, z, b가 모두 연속해서 나타나고, kin도 k, i, n이 연속해서 나타나기 때문에 그룹 단어이지만, aabbbccb는 b가 떨어져서 나타나기 때문에 그룹 단어가 아니다.
단어 N개를 입력으로 받아 그룹 단어의 개수를 출력하는 프로그램을 작성하시오.</p>
</blockquote>
<pre><code class="language-py">count = 0
for _ in range(int(input())):
    s = input()
    li = []
    for i in range(len(s)):
        if (s[i] in li) and s[i]!=s[i-1]:
            count -=1
            break
        else:
            li.append(s[i])
    count += 1
print(count)</code></pre>
<p>다른 풀이</p>
<pre><code class="language-py">#다른 풀이
n = int(input())
count = n
for i in range(n):
    s = input()
    for j in range(len(s)-1):
        if s[j] == s[j+1]:
            pass
        elif s[j] in s[j+1:]:
            count -= 1
            break
print(count)</code></pre>
<p><code>s[j] in s[j+1:]</code> 이렇게 쓰니까 더 편리하다..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BaekJoon/Python] 함수 : 15596, 4673, 1065]]></title>
            <link>https://velog.io/@suhyun-guri/BaekJoonPython-%ED%95%A8%EC%88%98-15596-4673-1065</link>
            <guid>https://velog.io/@suhyun-guri/BaekJoonPython-%ED%95%A8%EC%88%98-15596-4673-1065</guid>
            <pubDate>Sat, 22 Jan 2022 09:34:38 GMT</pubDate>
            <description><![CDATA[<h1 id="백준-단계-6-함수">백준 단계 6, 함수</h1>
<h2 id="15596-정수-n개의-합">#15596 정수 N개의 합</h2>
<blockquote>
<p>정수 n개가 주어졌을 때, n개의 합을 구하는 함수를 작성하시오.
Python 2, Python 3, PyPy, PyPy3: def solve(a: list) -&gt; int
a: 합을 구해야 하는 정수 n개가 저장되어 있는 리스트 (0 ≤ a[i] ≤ 1,000,000, 1 ≤ n ≤ 3,000,000)
리턴값: a에 포함되어 있는 정수 n개의 합 (정수)</p>
</blockquote>
<pre><code class="language-py">def solve(a:list)-&gt;int: 
    return sum(a)</code></pre>
<h2 id="4673-셀프-넘버">#4673 셀프 넘버</h2>
<blockquote>
<p>셀프 넘버는 1949년 인도 수학자 D.R. Kaprekar가 이름 붙였다. 양의 정수 n에 대해서 d(n)을 n과 n의 각 자리수를 더하는 함수라고 정의하자. 예를 들어, d(75) = 75+7+5 = 87이다.
양의 정수 n이 주어졌을 때, 이 수를 시작해서 n, d(n), d(d(n)), d(d(d(n))), ...과 같은 무한 수열을 만들 수 있다. 
예를 들어, 33으로 시작한다면 다음 수는 33 + 3 + 3 = 39이고, 그 다음 수는 39 + 3 + 9 = 51, 다음 수는 51 + 5 + 1 = 57이다. 이런식으로 다음과 같은 수열을 만들 수 있다.
33, 39, 51, 57, 69, 84, 96, 111, 114, 120, 123, 129, 141, ...
n을 d(n)의 생성자라고 한다. 위의 수열에서 33은 39의 생성자이고, 39는 51의 생성자, 51은 57의 생성자이다. 생성자가 한 개보다 많은 경우도 있다. 예를 들어, 101은 생성자가 2개(91과 100) 있다. 
생성자가 없는 숫자를 셀프 넘버라고 한다. 100보다 작은 셀프 넘버는 총 13개가 있다. 1, 3, 5, 7, 9, 20, 31, 42, 53, 64, 75, 86, 97
10000보다 작거나 같은 셀프 넘버를 한 줄에 하나씩 출력하는 프로그램을 작성하시오.</p>
</blockquote>
<pre><code class="language-py">original = set(range(1, 10001))
generated = set()
for i in range(1, 10001):
    for k in str(i):
        i += int(k)
    generated.add(i)

selfnumber = sorted(original - generated)
for i in selfnumber:
    print(i)</code></pre>
<h2 id="1065-한수">#1065 한수</h2>
<blockquote>
<p>어떤 양의 정수 X의 각 자리가 등차수열을 이룬다면, 그 수를 한수라고 한다. 등차수열은 연속된 두 개의 수의 차이가 일정한 수열을 말한다. N이 주어졌을 때, 1보다 크거나 같고, N보다 작거나 같은 한수의 개수를 출력하는 프로그램을 작성하시오. </p>
</blockquote>
<p>-&gt; 123의 숫자가 있다고 하면 자릿수대로 이 숫자들을 하나씩 분리한다. 그럼 1, 2, 3의 숫자가 된다. 이 숫자들은 등차수열을 이루므로 123은 한수이다.</p>
<pre><code class="language-py">num = int(input())
hansu = 0

for n in range(1, num+1):
    if n &lt; 100: #0~99까지는 모두 한수
        hansu += 1
    else:
        num_list = list(map(int, str(n))) #n을 하나씩 뜯어서 리스트화 : 123 -&gt; [1, 2, 3]
        if num_list[0] - num_list[1] == num_list[1] - num_list[2]: #등차수열 확인하는 과정
            hansu += 1
print(hansu)</code></pre>
<ul>
<li>함수로 구현한다면 ?<pre><code class="language-py">def hansu_num(num):
  hansu = 0
  for n in range(1, num+1):
      if n &lt; 100: #0~99까지는 모두 한수
          hansu += 1
      else:
          num_list = list(map(int, str(n))) #n을 하나씩 뜯어서 리스트화 : 123 -&gt; [1, 2, 3]
          if num_list[0] - num_list[1] == num_list[1] - num_list[2]: #등차수열 확인하는 과정
              hansu += 1
  return hansu
</code></pre>
</li>
</ul>
<p>num = int(input())
print(hansu_num(num))
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BaekJoon/Python] 1차원 배열 : 10818, 2562, 2577, 3052, 1546, 8958, 4344]]></title>
            <link>https://velog.io/@suhyun-guri/BaekJoonPython-1%EC%B0%A8%EC%9B%90-%EB%B0%B0%EC%97%B4-10818-2562-2577-3052-1546-8958-4344</link>
            <guid>https://velog.io/@suhyun-guri/BaekJoonPython-1%EC%B0%A8%EC%9B%90-%EB%B0%B0%EC%97%B4-10818-2562-2577-3052-1546-8958-4344</guid>
            <pubDate>Sat, 22 Jan 2022 09:10:36 GMT</pubDate>
            <description><![CDATA[<h1 id="백준-단계-5-1차원-배열">백준 단계 5, 1차원 배열</h1>
<h2 id="10818-최소-최대">#10818 최소, 최대</h2>
<blockquote>
<p>N개의 정수가 주어진다. 이때, 최솟값과 최댓값을 구하는 프로그램을 작성하시오.</p>
</blockquote>
<h4 id="👩🏻💻-my-code-">👩🏻‍💻 My Code :</h4>
<pre><code class="language-py"># 1. min, max 내장함수 사용
n = int(input())
mylist = list(map(int, input().split()))
print(min(mylist), max(mylist))</code></pre>
<pre><code class="language-py">#2. min, max 안 쓴 버전
n = int(input())
mylist = list(map(int, input().split()))
minnum = mylist[0]
maxnum = mylist[0]

for i in range(len(mylist)):
    if mylist[i] &lt; minnum:
        minnum = mylist[i]
    if mylist[i] &gt; maxnum:
        maxnum = mylist[i]
print(minnum, maxnum)</code></pre>
<h2 id="2562-최대값">#2562 최대값</h2>
<blockquote>
<p>9개의 서로 다른 자연수가 주어질 때, 이들 중 최댓값을 찾고 그 최댓값이 몇 번째 수인지를 구하는 프로그램을 작성하시오.
예를 들어, 서로 다른 9개의 자연수
<code>3, 29, 38, 12, 57, 74, 40, 85, 61</code>이 주어지면, 이들 중 최댓값은 85이고, 이 값은 8번째 수이다.</p>
</blockquote>
<h4 id="👩🏻💻-my-code--1">👩🏻‍💻 My Code :</h4>
<pre><code class="language-py"># 1. 처음 생각한 코드
mylist = [int(input()) for i in range(9)]
maxnum = max(mylist)
idx = 1
for i in mylist:
    if i == maxnum:
        print(maxnum)
        print(idx)
    idx += 1</code></pre>
<pre><code class="language-py"># 2. index 써서 더 간단히 !
mylist = [int(input()) for i in range(9)]
maxnum = max(mylist)
print(maxnum)
print(mylist.index(maxnum) + 1)</code></pre>
<h2 id="2577-숫자의-개수">#2577 숫자의 개수</h2>
<blockquote>
<p>세 개의 자연수 A, B, C가 주어질 때 A × B × C를 계산한 결과에 0부터 9까지 각각의 숫자가 몇 번씩 쓰였는지를 구하는 프로그램을 작성하시오. <br>
예를 들어 A = 150, B = 266, C = 427 이라면 A × B × C = 150 × 266 × 427 = 17037300 이 되고, 계산한 결과 17037300 에는 0이 3번, 1이 1번, 3이 2번, 7이 2번 쓰였다.</p>
</blockquote>
<h4 id="👩🏻💻-my-code--2">👩🏻‍💻 My Code :</h4>
<pre><code class="language-py">a = int(input())
b = int(input())
c = int(input())
x = list(str(a*b*c))

for i in range(10):
    print(x.count(str(i)))</code></pre>
<h2 id="3052-나머지">#3052 나머지</h2>
<blockquote>
<p>두 자연수 A와 B가 있을 때, A%B는 A를 B로 나눈 나머지 이다. 예를 들어, 7, 14, 27, 38을 3으로 나눈 나머지는 1, 2, 0, 2이다. <br>
수 10개를 입력받은 뒤, 이를 42로 나눈 나머지를 구한다. 그 다음 서로 다른 값이 몇 개 있는지 출력하는 프로그램을 작성하시오.</p>
</blockquote>
<h4 id="👩🏻💻-my-code--3">👩🏻‍💻 My Code :</h4>
<pre><code class="language-py">mylist = [int(input()) for _ in range(10)]
value = [i%42 for i in mylist]
print(len(set(value)))</code></pre>
<p>=&gt; 리스트를 집합(set)으로 바꿔주면 중복이 제거되고 unique한 값만 남는다 !</p>
<h2 id="1546-평균">#1546 평균</h2>
<blockquote>
<p>세준이는 기말고사를 망쳤다. 세준이는 점수를 조작해서 집에 가져가기로 했다. 일단 세준이는 자기 점수 중에 최댓값을 골랐다. 이 값을 M이라고 한다. 그리고 나서 모든 점수를 점수/M*100으로 고쳤다.
예를 들어, 세준이의 최고점이 70이고, 수학점수가 50이었으면 수학점수는 50/70*100이 되어 71.43점이 된다.
세준이의 성적을 위의 방법대로 새로 계산했을 때, 새로운 평균을 구하는 프로그램을 작성하시오.</p>
</blockquote>
<h4 id="👩🏻💻-my-code--4">👩🏻‍💻 My Code :</h4>
<pre><code class="language-py">n = int(input())
score = list(map(int, input().split()))
maxscore = max(score)
newscore = [i / maxscore*100 for i in score]
newmean = sum(newscore) / n
print(newmean)</code></pre>
<h2 id="8958-ox퀴즈">#8958 OX퀴즈</h2>
<blockquote>
<p>&quot;OOXXOXXOOO&quot;와 같은 OX퀴즈의 결과가 있다. O는 문제를 맞은 것이고, X는 문제를 틀린 것이다. 문제를 맞은 경우 그 문제의 점수는 그 문제까지 연속된 O의 개수가 된다. 예를 들어, 10번 문제의 점수는 3이 된다.
&quot;OOXXOXXOOO&quot;의 점수는 1+2+0+0+1+0+0+1+2+3 = 10점이다.
OX퀴즈의 결과가 주어졌을 때, 점수를 구하는 프로그램을 작성하시오.</p>
</blockquote>
<h4 id="👩🏻💻-my-code--5">👩🏻‍💻 My Code :</h4>
<pre><code class="language-py">#8958
n = int(input())

for _ in range(n):
    li = list(input())
    score, final_score = 0, 0
    for i in li:
        if i == &#39;O&#39;:
            score += 1
            final_score += score
        elif i == &#39;X&#39;:
            score = 0
    print(final_score)</code></pre>
<h2 id="4344-평균은-넘겠지">#4344 평균은 넘겠지</h2>
<blockquote>
<p>각 케이스마다 한 줄씩 평균을 넘는 학생들의 비율을 반올림하여 소수점 셋째 자리까지 출력한다.</p>
</blockquote>
<h4 id="👩🏻💻-my-code--6">👩🏻‍💻 My Code :</h4>
<pre><code class="language-py">n = int(input())
for i in range(n):
    li = list(map(int, input().split()))
    avg = sum(li[1:]) / li[0]
    count = 0
    for score in li[1:]:
        if score &gt; avg:
            count += 1
    print(&quot;{:.3f}%&quot;.format(count / li[0] * 100))</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Permutation Feature Importance]]></title>
            <link>https://velog.io/@suhyun-guri/Permutation-Feature-Importance</link>
            <guid>https://velog.io/@suhyun-guri/Permutation-Feature-Importance</guid>
            <pubDate>Tue, 18 Jan 2022 12:16:26 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://christophm.github.io/interpretable-ml-book/feature-importance.html">8.5 Permutation Feature Importance | Interpretable Machine Learning</a>
이 글은 위 문서를 번역, 요약한 내용입니다.</p>
</blockquote>
<h1 id="permutation-feature-importance">Permutation Feature Importance</h1>
<p><strong>Permutation Feature Importance (순열 피처 중요도)</strong></p>
<ul>
<li>feature values <em>(실제 결과와 그 feature 사이의 관계를 끊는 (즉, 무작위로 섞어서 관계 없게 만든다는 뜻))</em>를 permuted(바꿔 넣은) 후 모델 예측 error 증가를 측정하여 실제 error와 비교</li>
</ul>
<h2 id="1-theory">1. Theory</h2>
<p>이론은 간단하다.</p>
<p>피처를 바꿔 넣은 후 모델의 예측 오차의 증감을 계산하여 중요도를 측정한다. 모델이 예측을 위해 피처에 의존했으므로 오차가 증가했을 경우 그 피처는 중요하다. 반면에 오차가 동일하다면 그 피처는 중요하지 않다.</p>
<p><img src="https://images.velog.io/images/suhyun-guri/post/7727432d-27f1-41c9-8e47-3597195e0e79/image.png" alt=""></p>
<p><strong>Fisher, Rudin, and Dominici (2018)</strong> 에서 피처 j를 바꿔 넣는 것 대신에 데이터셋을 반으로 나누고 두 데이터셋의 피처 j값을 교환하는 것을 제안했다.</p>
<p>생각해보면 피처 j를 바꿔 넣은 것과 정확히 같다.</p>
<p>더 정확한 측정치를 원한다면, 각 인스턴스를 서로 다른 인스턴스의 피처 j 값과 쌍으로 구성하여 피처 j의 오차를 측정할 수 있다. (자신 제외)</p>
<p>이렇게 하면 <code>n(n-1)</code> 사이즈의 데이터셋을 얻게 되며 이는 많은 양의 계산 시간이 필요하다.  </p>
<p>극도로 정확한 측정치를 얻고 싶다면 <code>n(n-1)</code> -method 를 추천한다. </p>
<h2 id="2-should-i-compute-importance-on-training-or-test-data">2. Should I Compute Importance on Training or Test Data?</h2>
<p>Answering the question about training or test data touches the fundamental question of what feature importance is.</p>
<h3 id="2-1-the-case-for-test-data">2-1. The case for test data</h3>
<p>모델이 훈련된 동일한 데이터에 대해 오차를 측정하면, 일반적으로 측정값이 너무 optimisitic(낙관적)이기 때문에 모델이 실제보다 훨씬 더 잘 작동하는 것처럼 보인다.</p>
<p>Permutation Feature Importance는 모델 오차 측정에 의존하기 때문에 우리는 보이지 않는 테스트 데이터를 사용해야 한다.</p>
<p>훈련 데이터에 기초한 Feature Importance는 실제로 모델이 오버피팅되고 피처가 전혀 중요하지 않은 경우에 그 피처가 예측에 있어서 중요하다고 믿게 한다.</p>
<h3 id="2-2-the-case-for-train-data">2-2. The case for train data</h3>
<p>The arguments for using training data are somewhat more difficult to formulate, but are IMHO just as compelling as the arguments for using test data.</p>
<p>훈련 데이터에 기반한 Feature Importance는 예측을 하기 위해 모델에 의존한다는 점에서 중요한 피처를 알려준다.</p>
<p>In practice, you want to use all your data to train your model to get the best possible model in the end. This means no unused test data is left to compute the feature importance. You have the same problem when you want to estimate the generalization error of your model. If you would use (nested) cross-validation for the feature importance estimation, you would have the problem that the feature importance is not calculated on the final model with all the data, but on models with subsets of the data that might behave differently.</p>
<p><strong>However, in the end I recommend to use test data for permutation feature importance.</strong></p>
<h2 id="3-advantages">3. Advantages</h2>
<p>Nice interpretation : Feature Importance는 피처의 정보가 파괴되었을 때 모델 오차는 증가하는 것이다.</p>
<p>FI는 모델의 행동에 대해 매우 압축되고 global한 인사이트를 제공한다.</p>
<p>error difference 대신 error ratio를 사용하는 긍정적인 측면은 FI 측정이 서로 다른 문제에서 비교 가능하다는 것이다. → 뭔소리?</p>
<p>FI는 다른 피처와의 모든 상호작용을 자동으로 고려한다. 피처를 바꾸면서 다른 피처의 상호작용 효과도 파괴된다. 즉, Permutation FI는 메인 피처 효과와 모델 성능에 있어서 상호작용 효과 모두 고려한다. 이는 또한 단점도 된다. 두 피처의 중요도 측정에 두 피처의 상호작용 중요성이 포함되기 때문이다.</p>
<p>Permutation FI는 모델을 retraining할 필요가 없다.</p>
<p>일부 다른 방법에서는 피처를 제거하고 모델을 다시 훈련시킨 후 모델 오차를 비교하는 것을 제안한다. ml 모델의 재훈련은 시간이 오래 걸릴 수 있기 때문에, 오직 한 피처를 바꾸는 것은 많은 시간을 절약할 수 있다.</p>
<p>일부 피처들을 가지고 모델을 재훈련 시키는 방식은 직관적으로 보이지만, 줄어든 데이터를 사용한 모델은 FI에 의미가 없다.</p>
<p>우리는 고정 모델의 FI에 관심이 있다. 줄어든 데이터셋으로 재훈련시키면 우리가 관심 있는 모델과는 다른 모델이 된다. </p>
<h2 id="4-disadvantages">4. Disadvantages</h2>
<p>Permutation FI는 모델의 오차와 연결된다. 본질적으로 나쁜 것은 아니지만, 어떤 경우에는 필요한 것이 아니다.</p>
<p>어떤 경우에는 성능에 대한 의미를 고려하지 않고 모델의 Output이 피처에 따라 얼마나 달라지는지 알고 싶을 수 있다. </p>
<p>예를 들어 성능이 얼마나 감소하는지보다 각 피처에 의해 모델 output 분산 중 어느 정도가 설명되는지에 관심이 있는 경우이다.  </p>
<p>Model variance (explained by the features) and feature importance correlate strongly when the model generalizes well (i.e. it does not overfit).</p>
<p>If someone only provides you with the model and unlabeled data – but not the true outcome – you cannot compute the permutation feature importance.</p>
<p>만약 피처들이 상관관계가 있다면, Permutation FI는 비현실적인 데이터 인스턴스에 의해 편향될 수 있다.</p>
<p>피처들이 강하게 상관되어 있는지 확인하고 그럴 경우 FI에 대한 해석에 주의하라. 그러나 양방향 상관관계로는 문제를 드러내기에 충분하지 않을 수 있다.</p>
<h2 id="5-alternatives">5. Alternatives</h2>
<p>Permutation FI에 대한 불가지론적(agnostic) 대안은 분산 기반 측정이다.</p>
<p>Sobol의 지수 또는 기능 분산 분석과 같은 분산 기반 FI 측정은 예측 함수의 높은 분산을 유발하는 피처에 더 높은 중요도를 부여한다.</p>
<p>또한 SHAP Importance는 분산 기반 FI와 유사하다. 피처를 변경하면 Output이 크게 달라진다. 이러한 중요도 정의는 Permutation FI의 경우와 같은 손실 기반 정의와 다르다. 이는 모델이 오버피팅되었을 때 명백하다.</p>
<p>모델이 Output과 관련이 없는 피처를 사용하고 오버핏되었을 때, Permutation FI는 이 피처가 알맞은 예측을 생성하는데 기여하지 않기 때문에 중요도 0을 부여한다. 반면에 분산 기반 FI는 피처가 변경될 때 예측이 많이 변경될 수 있으므로 피처의 중요도가 높게 측정될 수 있다.</p>
<blockquote>
</blockquote>
<p>⚠️ <strong>3줄요약</strong></p>
<ol>
<li>feature values <span style="color:grey"><em>(실제 결과와 그 feature 사이의 관계를 끊는 (즉, 무작위로 섞어서 관계 없게 만든다는 뜻))</em></span>를 permuted(바꿔 넣은) 후 모델 예측 error 증가를 측정하여 실제 error와 비교</li>
<li>Permutation Feature Importance는 모델 오차 측정에 의존하기 때문에 우리는 보이지 않는 테스트 데이터를 사용해야 한다.</li>
<li>피처들이 강하게 상관되어 있는지 확인하고 그럴 경우 FI에 대한 해석에 주의하라.</li>
</ol>
</aside>]]></description>
        </item>
        <item>
            <title><![CDATA[장고 프로젝트에서 앱 개발하기]]></title>
            <link>https://velog.io/@suhyun-guri/%EC%9E%A5%EA%B3%A0-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90%EC%84%9C-%EC%95%B1-%EA%B0%9C%EB%B0%9C%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@suhyun-guri/%EC%9E%A5%EA%B3%A0-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90%EC%84%9C-%EC%95%B1-%EA%B0%9C%EB%B0%9C%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 16 Jan 2022 15:03:49 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-두-개의-앱-만들기">📌 두 개의 앱 만들기</h1>
<p>모든 장고 프로젝트는 1개 이상의 앱으로 구성된다.</p>
<p><code>앱</code> : 특정한 기능을 수행하는 단위 모듈</p>
<p>2개의 앱 - 블로그 기능을 위한 blog 앱, 대문 페이지와 자기소개 페이지를 보여주기 위한 single_pages 앱을 만들 예정이다.
실행하기 전, 가상환경이 실행되고 있는지 확인 후 코드 작성</p>
<pre><code class="language-bash">python manage.py startapp blog
python manage.py startapp single_pages</code></pre>
<p>위 코드를 실행하면 파이참에 2개의 폴더가 생성되어 있고 각 폴더에는 <code>admin.py</code>, <code>apps.py</code>, <code>models.py</code>, <code>test.py</code>, <code>views.py</code>와 같은 파일들이 생성된다.
이처럼 앱은 독립된 파일들로 구성되어 독립된 기능을 한다.</p>
<h1 id="📌-모델-만들기">📌 모델 만들기</h1>
<p>장고의 장점 중 하나는 모델을 이용해 장고 웹 프레임워크 안에서 데이터베이스를 관리할 수 있다는 것이다. 파이썬만으로 CRUD 기능을 쉽게 구현할 수 있을 뿐만 아니라 관리자 페이지, 입력 폼 등도 쉽게 만들 수 있다.</p>
<h3 id="post-모델">Post 모델</h3>
<p><code>blog/models.py</code></p>
<pre><code class="language-python">from django.db import models

# Create your models here.
class Post(models.Model):
    title = models.CharField(max_length = 30)
    content = models.TextField()
    created_at = models.DateTimeField()
    #author 추후 작성</code></pre>
<p><code>do_it_django_prj/settings.py</code> - 프로젝트에 생성한 앱 등록</p>
<pre><code class="language-python"># Application definition

INSTALLED_APPS = [
    &#39;django.contrib.admin&#39;,
    &#39;django.contrib.auth&#39;,
    &#39;django.contrib.contenttypes&#39;,
    &#39;django.contrib.sessions&#39;,
    &#39;django.contrib.messages&#39;,
    &#39;django.contrib.staticfiles&#39;,
    &#39;blog&#39;,
    &#39;single_pages&#39;
]</code></pre>
<p>아직 Post 모델은 파이썬 클래스로 존재한다. 이를 데이터베이스에 반영해야 실제 테이블이 생성된다.</p>
<ul>
<li>데이터베이스에 반영하기 위해</li>
</ul>
<pre><code class="language-python">python manage.py makemigrations</code></pre>
<ul>
<li>실제 데이터베이스에 모델을 적용하려면</li>
</ul>
<pre><code class="language-python">python manage.py migrate</code></pre>
<p><img src="https://images.velog.io/images/suhyun-guri/post/f3d7d6ea-030d-43d3-ac4c-638b4a713863/image.png" alt="">
++ <code>gitignore</code>에 migration 추가 !</p>
<ul>
<li>왜 gitignore에 추가할까? = 왜 깃으로 관리하지 않을까<ul>
<li>개발하다 보면 <code>models.py</code>를 수정할 일이 많을 것이다. 그리고 최종 결과물만 서버에 적용</li>
<li>그런데 모델 수정 내역을 일일이 기록하면 로컬 컴퓨터의 데이터베이스와 서버의 데이터베이스가 일치하지 않아 문제가 생길 수 있다.</li>
</ul>
</li>
</ul>
<p><code>blog/admin.py</code> - 관리자 페이지에 Post 모델을 등록</p>
<pre><code class="language-python">from django.contrib import admin
from .models import Post

# Register your models here.
admin.site.register(Post)</code></pre>
<p>BLOG라는 섹션이 생기고 그 아래에 Posts라는 메뉴도 생겼다.
<img src="https://images.velog.io/images/suhyun-guri/post/c0b573c0-547a-4ed0-9fec-473d13a93281/image.png" alt=""></p>
<h3 id="post-모델-수정">Post 모델 수정</h3>
<p><code>blog/models.py</code> - 제목 형식 지정하는 함수 추가</p>
<pre><code class="language-python">class Post(models.Model):
    title = models.CharField(max_length = 30)
    content = models.TextField()
    created_at = models.DateTimeField()

    #제목나오도록 함수 생성
    def __str__(self):
    return f&#39;[{self.pk}]{self.title}&#39;</code></pre>
<p><img src="https://images.velog.io/images/suhyun-guri/post/a5338f51-5743-4d53-b080-347bd72a62fc/image.png" alt=""></p>
<p><code>settings.py</code> - 시간 서울 시간으로 변경</p>
<pre><code class="language-python">TIME_ZONE = &#39;Asia/Seoul&#39;</code></pre>
<p><img src="https://images.velog.io/images/suhyun-guri/post/ab0b37a4-ab1b-4d56-8252-166afe76e04d/image.png" alt=""></p>
<p>자동으로 작성 시각과 수정 시각 저장하기 !</p>
<pre><code class="language-python">class Post(models.Model):
    title = models.CharField(max_length = 30)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    #제목나오도록 함수 생성
    def __str__(self):
    return f&#39;[{self.pk}]{self.title}&#39;</code></pre>
<p>모델을 수정했으므로 다시 makemigrations로 장고에게 알려주고 migrate로 데이터베이스에 반영</p>
<pre><code class="language-python">python manage.py makemigrations
python manage.py migrate</code></pre>
<blockquote>
<p>&#39;Do it 장고+부트스트랩 파이썬 웹 개발의 정석&#39;을 통해 학습한 내용입니다.
<a href="http://www.yes24.com/Product/Goods/96541859">Do it 장고+부트스트랩 파이썬 웹 개발의 정석 도서링크</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Django 프로젝트 생성하기]]></title>
            <link>https://velog.io/@suhyun-guri/Django-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@suhyun-guri/Django-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 16 Jan 2022 14:45:25 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-먼저-mtv-패턴이란">📌 먼저, MTV 패턴이란?</h1>
<p>장고로 만든 웹사이트는 모델로 자료의 형태를 정의하고 뷰로 어떤 자료를 어떤 동작으로 보여줄지 정의하고, 템플릿으로 웹페이지에서 출력할 모습을 정의한다.</p>
<p>이러한 작동 구조를 줄여서 MTV 패턴이라고 부른다.</p>
<p>이렇게 분리해서 웹 사이트 기능을 관리함으로써 프론트엔드 개발자는 HTML을 비롯한 화면 구성에 집중할 수 있게 되고, 백엔드 개발자도 화면 뒤의 작업에 집중할 수 있게 된다.</p>
<h1 id="📌-django-프로젝트-생성하기">📌 Django 프로젝트 생성하기</h1>
<p>필요한 것 : Cmber, Pycharm</p>
<ol>
<li>Cmber에서 github 저장소 클론</li>
<li>파이참에서 가상환경 생성</li>
</ol>
<ul>
<li><p>cmber(터미널)에서 가상환경 실행과 종료</p>
<pre><code class="language-bash">#venv activate
venv\Scripts\activate.bat
#deactivate
deactivate</code></pre>
</li>
<li><p>초기 설치 목록</p>
</li>
</ul>
<pre><code class="language-bash">pip install django</code></pre>
<ul>
<li>장고 프로젝트 생성하기</li>
</ul>
<pre><code class="language-bash">#이 폴더에 장고 프로젝트를 만들자. - dot(.) 필수 !
django-admin startproject do_it_django_prj .

#서버 실행하기
python manage.py runserver</code></pre>
<p><img src="https://images.velog.io/images/suhyun-guri/post/bef167d3-46aa-4076-a51d-a6be752ddf0f/image.png" alt="">
localhost 접속 시 이러한 창이 뜨면 성공 !</p>
<h2 id="📍-migration">📍 Migration</h2>
<p>장고에서 마이그레이션(migration)이란 데이터베이스에 적용시켜야 하는 변화에 대한 기록이다.</p>
<p>장고는 새 프로젝트를 생성할 때, 데이터베이스에 기본적으로 필요한 테이블을 미리 마련해 둔다.</p>
<h3 id="데이터베이스-생성하기">데이터베이스 생성하기</h3>
<pre><code class="language-bash">python manage.py migrate</code></pre>
<p><code>db.sqlite3</code>이라는 파일이 새로 생성되고, 그 안에 마이그레이션을 반영한 데이터베이스가 생성된다.</p>
<h3 id="관리자-계정-생성하기-localhost8888admin으로-접속">관리자 계정 생성하기 (localhost:8888/admin으로 접속)</h3>
<pre><code class="language-bash">python manage.py createsuperuser
#아이디와 비번 설정</code></pre>
<ul>
<li>admin 접속하여 로그인하면 보이는 화면
<img src="https://images.velog.io/images/suhyun-guri/post/79c8b6bd-511c-4d18-a19b-6f4733a2de77/image.png" alt=""></li>
</ul>
<blockquote>
<p>&#39;Do it 장고+부트스트랩 파이썬 웹 개발의 정석&#39;을 통해 학습한 내용입니다.
<a href="http://www.yes24.com/Product/Goods/96541859">Do it 장고+부트스트랩 파이썬 웹 개발의 정석 도서링크</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 상태 관리]]></title>
            <link>https://velog.io/@suhyun-guri/React-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@suhyun-guri/React-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Sun, 16 Jan 2022 11:10:15 GMT</pubDate>
            <description><![CDATA[<h1 id="1-상태-관리">1. 상태 관리</h1>
<ul>
<li>상태 관리 기술이란, 앱 상에서의 데이터를 메모리 등에 저장하고 하나 이상의 컴포넌트에서 데이터를 공유하는 것이다.</li>
<li>한 컴포넌트 안에서의 상태, 여러 컴포넌트 간의 상태, 전체 앱의 상태 관리를 모두 포함한다.</li>
<li>상태가 많지 않거나, 유저와의 인터렉션이 많지 않다면 매 작업 시 서버와 동기화하더라도 충분하다</li>
<li>BUT 사용하는 데이터가 점점 많아지고 유저와의 인터렉션 시 임시로 저장하는 데이터가 많아지는 경우, 상태 관리를 고려해야 한다.</li>
</ul>
<h4 id="장단점">장단점</h4>
<ul>
<li>장점 :<ul>
<li>높은 품질의 코드를 작성하는 데 유리</li>
<li>성능 최적화, 네트워크 최적화 등에 유리</li>
<li>데이터 관리의 고도화</li>
</ul>
</li>
<li>단점 :<ul>
<li>Boilerplate 문제<ul>
<li>what? 최소한의 변경으로 여러 곳에서 재사용되며, 반복적으로 비슷한 형태를 띄는 코드를 의미</li>
</ul>
</li>
<li>파악해야 할 로직과 레이어가 많아진다.</li>
<li>잘못 사용할 경우, 앱의 복잡도만을 높이거나 성능을 악화시킨다.</li>
</ul>
</li>
</ul>
<h2 id="상태-관리가-해결해야-할-문제들">상태 관리가 해결해야 할 문제들</h2>
<h3 id="데이터-캐싱과-재활용">데이터 캐싱과 재활용</h3>
<ul>
<li>SPA에서 페이지 로딩 시마다 모든 데이터를 로딩한다면, 사용자 경험 측면에서 MPA를 크게 넘어서기 힘들다.</li>
<li>오히려 네트워크 요청 수가 많아져 더 느릴 수도 있다.</li>
<li>변경이 잦은 데이터가 아니라면, 데이터를 캐싱하고 재활용함</li>
<li>변경이 잦다면, 데이터의 변경 시점을 파악해 최적화<ul>
<li>ex) 일정 시간마다 서버에 저장, 타이핑 5초 후 서버에 저장</li>
</ul>
</li>
</ul>
<h3 id="prop-drilling">Prop Drilling</h3>
<ul>
<li>컴포넌트가 복잡해지는 경우, 상위 부모와 자식 컴포넌트 간의 깊이가 커짐</li>
<li>최하단의 자식 컴포넌트가 데이터를 쓰기 위해 최상위 컴포넌트부터 데이터를 보내야 하는 상황이 발생</li>
<li>Context API 등을 활용, 필요한 컴포넌트에서 데이터를 가져올 수 있음</li>
<li><strong>컴포넌트 간의 결합성을 낮춤</strong></li>
</ul>
<h1 id="2-flux-pattern">2. Flux Pattern</h1>
<p>: 2014년 Facebook에서 제안한 웹 애플리케이션 아키텍처 패턴</p>
<ul>
<li><p>Unidirectional data flow(일방향 데이터 흐름)를 활용, 데이터의 업데이트와 UI 반영을 단순화</p>
</li>
<li><p>React의 UI 패턴인 합성 컴포넌트와 어울리도록 설계</p>
</li>
<li><p>redux, react-redux 라이브러리의 Prior art.</p>
<h3 id="flux-pattern-vs-mvc-pattern">Flux Pattern vs MVC Pattern</h3>
</li>
<li><p>MVC : Model View Controller ⇒ Bidirectional data flow (양방향 데이터 흐름)</p>
</li>
<li><p>MVC 패턴에서는, View에서 특정 데이터를 업데이트하면 연쇄적인 업데이트가 일어남 - Bidirectional data flow이기 때문에</p>
</li>
<li><p>특정 유저의 인터렉션이 여러 UI 컴포넌트가 사용하는 데이터에 영향을 줄 때, MVC만으로는 앱의 복잡도를 낮추거나 업데이트의 흐름을 따라가기 힘들다</p>
</li>
<li><p><strong>Flux는 하나의 Action(유저 인터렉션)이 하나의 Update만을 만들도록 한다.</strong></p>
</li>
<li><p>data와 업데이트가 한 방향으로 흐르므로 UI의 업데이트를 예측하기 쉬움</p>
</li>
</ul>
<h3 id="flux-구조">Flux 구조</h3>
<ul>
<li>Action → Dispatcher → Store → View 순으로 데이터가 흐른다</li>
<li>store는 미리 dispatcher에 callback을 등록해 자신이 처리할 action을 정의</li>
<li>action creator는 action을 생성하여 dispatcher로 보냄</li>
<li>dispatcher는 action을 store로 넘김</li>
<li>store는 action에 따라 데이터를 업데이트 후, 관련 view로 변경 이벤트 발생</li>
<li>View는 그에 따라 데이터를 다시 받아와 새로운 UI를 만듦</li>
<li>유저 인터렉션이 발생하면 View는 action을 발생</li>
</ul>
<h1 id="3-usestate-useref-usecontext-usereducer">3. useState, useRef, useContext, useReducer</h1>
<h3 id="상태-관리에-사용되는-훅">상태 관리에 사용되는 훅</h3>
<ul>
<li>외부 라이브러리 없이 React가 제공하는 훅만으로 상태 관리를 구현하기 위해 사용</li>
<li>함수형 컴포넌트에 상태를 두고, 여러 컴포넌트 간 데이터와 데이터 변경 함수를 공유하는 방식으로 상태를 관리하게 됨.<h2 id="usestate">UseState</h2>
</li>
<li>단순한 하나의 상태를 관리하기에 적합</li>
<li><code>const [state, setState] = useState(초기값 or 초기함수)</code></li>
<li>state가 바뀌면, state를 사용하는 컴포넌트를 리렌더링</li>
<li>useEffect와 함께, state에 반응하는 훅을 구축<h2 id="useref">useRef</h2>
</li>
<li>상태가 바뀌어도 리렌더링하지 않는 상태를 정의함</li>
<li>즉, 상태가 UI의 변경과 관계없을 때 사용 (UI의 변경이 필요 없을 때)<ul>
<li>ex) setTimer의 timerid 저장</li>
</ul>
</li>
<li>uncontrolled component의 상태를 저장하는 등 리렌더링을 최소화하는 상태 관리에서 사용됨 ( 필요할 때만 데이터를 불러오는 것 )<h2 id="usecontext">useContext</h2>
</li>
<li>컴포넌트와 컴포넌트 간 상태를 공유할 때 사용</li>
<li>부분적인 컴포넌트들의 상태 관리, 전체 앱의 상태 관리를 모두 구현</li>
<li>Context Provider 안에서 렌더링되는 컴포넌트는 useContext를 이용해 깊이 nested(중첩)된 컴포넌트라도 바로 context value를 가져옴</li>
<li>Context value가 바뀌면, 내부 컴포넌트는 모두 리렌더링됨<ul>
<li>데이터를 변경할 때 주의할 점임<h2 id="usereducer">useReducer</h2>
</li>
</ul>
</li>
<li>useState보다 복잡한 상태를 다룰 때 사용</li>
<li>별도의 라이브러리 없이 flux pattern에 기반한 상태 관리를 구현</li>
<li><code>const [state, dispatch] = useReducer(reducer, initState)</code><ul>
<li>dispatch(action) =&gt; dispatch에 action을 넘기면 reducer로 흘러간다.</li>
<li>reducer에서 업데이트된 state를 리턴하고 그 후 state가 업데이트 된다.</li>
<li>그 후 이 state를 쓰는 다양한 컴포넌트들이 업데이트 된다.</li>
</ul>
</li>
<li>nested state 등 복잡한 여러 개의 상태를 한꺼번에 관리하거나, 어떤 상태에 여러 가지 처리를 적용할 때 유용</li>
<li>상태가 복잡하다면, useState에 관한 callback을 내려주는 것보다 dispatch를 prop으로 내려 리렌더링을 최적화하는 것을 권장</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[과적합 문제 방지 기법]]></title>
            <link>https://velog.io/@suhyun-guri/%EA%B3%BC%EC%A0%81%ED%95%A9-%EB%AC%B8%EC%A0%9C-%EB%B0%A9%EC%A7%80-%EA%B8%B0%EB%B2%95</link>
            <guid>https://velog.io/@suhyun-guri/%EA%B3%BC%EC%A0%81%ED%95%A9-%EB%AC%B8%EC%A0%9C-%EB%B0%A9%EC%A7%80-%EA%B8%B0%EB%B2%95</guid>
            <pubDate>Sat, 15 Jan 2022 10:50:24 GMT</pubDate>
            <description><![CDATA[<p>딥러닝 모델 학습에서의 과적합 방지 기법으로는 정규화, 드롭아웃, 배치 정규화가 대표적이다.</p>
<h1 id="🌞-정규화-regularization">🌞 정규화 (Regularization)</h1>
<p>모델이 복잡해질수록 parameter들은 많아지고, 절댓값이 커지는 경향이 발생한다.</p>
<p>이는 기존 손실함수에 규제항을 더해 최적값을 찾을 수 있다.</p>
<p>대표적으로 L1, L2 규제가 있다.</p>
<h2 id="l1-정규화lasso-regularization">L1 정규화(Lasso Regularization)</h2>
<ul>
<li>$TotalLoss = Loss + \lambda \sum_w |W|$</li>
<li>가중치의 절댓값의 합을 더한 값에 규제 강도 λ를 곱하여 오차에 더한다.</li>
<li>즉, 가중치(w)의 절댓값에 비례하는 손실(loss)이 기존 손실 함수에 추가되는 형태</li>
<li>어떤 가중치(w)는 실제로 0이 된다. 즉, 모델에서 완전히 제외되는 특성이 생기는 것이다.</li>
<li>이를 통해 모델을 일반화시킨다. 다른 말로 Sparse Model을 만든다라고 한다.</li>
<li><code>tf.keras.layers.Dense(kernel_regularizer = tf.keras.regularizers.l1(ratio))</code><ul>
<li>ratio : 가중치에 L1 정규화를 적용하는 비율 (0.001 ~ 0.005)</li>
</ul>
</li>
</ul>
<h2 id="l2-정규화-ridge-regularization">L2 정규화 (Ridge Regularization)</h2>
<ul>
<li>$TotalLoss = Loss + \lambda \sum_w W^2$</li>
<li>각 가중치 제곱의 합에 규제 강도 λ를 곱한다.</li>
<li>즉, 가중치의 제곱에 비례하는 손실이 기존 손실 함수에 추가되는 형태</li>
<li>λ를 크게 하면 가중치가 더 많이 감소되고, λ를 작게 하면 가중치가 증가한다.</li>
<li>학습이 진행될 때 가중치의 값이 0에 가까워지도록 만들어준다. 가중치를 0으로 만드는 L1 정규화와는 차이가 있다.</li>
<li>이를 통해 특정 가중치에 치중되지 않도록 가중치 값을 조율하게 되며 가중치 감쇠(Weight Decay)라고도 부른다.</li>
<li><code>tf.keras.layers.Dense(kernel_regularizer = tf.keras.regularizers.l2(ratio))</code><ul>
<li>ratio : 가중치에 L2 정규화를 적용하는 비율 (0.001 ~ 0.005)</li>
</ul>
</li>
</ul>
<h1 id="🌞-dropout">🌞 Dropout</h1>
<p><img src="https://images.velog.io/images/suhyun-guri/post/cb5105e4-c889-43f2-92fb-6f4b0d78335d/dropout.png" alt=""></p>
<ul>
<li>각 layer마다 일정 비율의 뉴런을 랜덤하게 drop시켜(0으로 만들어) 나머지 뉴런들만 학습하는 방법이다.</li>
<li>Dropout을 적용하면 학습되는 노드와 가중치들이 매번 달라진다.</li>
<li>이를 통해 모델 내부의 특정 가중치에 치중되는 것을 막고 일반화된 모델을 만들 수 있다.</li>
<li>주의할 점은 학습이 끝난 후 테스트 과정에서는 Dropout을 사용하면 안된다는 점이다.</li>
<li><code>tf.keras.layers.Dropout(prob)</code><ul>
<li>prob : 드롭 아웃을 적용할 확률 (0.1 ~ 0.5)</li>
</ul>
</li>
</ul>
<h1 id="🌞-배치-정규화-batch-normalization">🌞 배치 정규화 (Batch Normalization)</h1>
<p><img src="https://images.velog.io/images/suhyun-guri/post/5a6e9509-3af5-4ba3-b39c-f82057d284e4/batchnarmalization.png" alt=""></p>
<ul>
<li>배치 정규화 (Batch Normalization)는 정규화를 모델에 들어가는 입력 데이터뿐만 아니라 모델 내부 은닉층의 입력 노드에도 적용하는 것이다.</li>
<li>배치 정규화를 적용하면 매 층마다 정규화를 진행하므로 가중치 초기값에 크게 의존하지 않는다. 즉, 가중치 초기화의 중요도가 감소한다.</li>
<li>또한 과적합을 억제하므로 Dropout과 L1, L2 정규화의 필요성이 감소한다.</li>
<li>가장 큰 장점은 학습 속도가 빨라진다는 것이다.</li>
<li>배치 정규화는 하나의 Layer로써 Dense Layer와 활성화 함수 사이에서 작용한다.</li>
<li>그래서 적용을 할 때,<pre><code class="language-py">tf.keras.models.Sequential([
          tf.keras.layers.Flatten(input_shape=(28, 28)),
          tf.keras.layers.Dense(128),
          tf.keras.layers.BatchNormalization(),
          tf.keras.layers.Activation(&#39;relu&#39;),
          tf.keras.layers.Dense(64),
          tf.keras.layers.BatchNormalization(),
          tf.keras.layers.Activation(&#39;relu&#39;),
          tf.keras.layers.Dense(1, activation=&#39;softmax&#39;)])</code></pre>
</li>
</ul>
<p>이런 식으로 Activation Layer를 따로 사용하여 Dense와 Activation 사이에 넣어야 한다.!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[퍼셉트론, 신경망, 활성화 함수]]></title>
            <link>https://velog.io/@suhyun-guri/%EC%8B%A0%EA%B2%BD%EB%A7%9D</link>
            <guid>https://velog.io/@suhyun-guri/%EC%8B%A0%EA%B2%BD%EB%A7%9D</guid>
            <pubDate>Tue, 11 Jan 2022 08:24:45 GMT</pubDate>
            <description><![CDATA[<p>퍼셉트론은 복잡한 함수를 표현할 수 있지만, 가중치를 설정하는 작업은 사람이 수동으로 해야 한다.</p>
<p>신경망은 퍼셉트론과 다르게 가중치 매개변수의 적절한 값을 데이터로부터 자동으로 학습하는 능력이 있다.</p>
<h1 id="1-신경망">1. 신경망</h1>
<p>신경망은 입력층, 은닉층, 출력층으로 구성되어 있다.</p>
<p><img src="https://images.velog.io/images/suhyun-guri/post/ea29b630-4aaa-40ac-9027-6fd5e966da9a/%EC%8B%A0%EA%B2%BD%EB%A7%9D.png" alt=""></p>
<p>은닉층의 뉴런은 눈에 보이지 않는다.</p>
<p>은닉층이 1개일 때, 입력층은 0층, 은닉층은 1층, 출력층은 2층이라고 한다.</p>
<p><em>은닉층이 1개일 때 신경망은 모두 3층으로 구성되지만 가중치를 갖는 층은 2개뿐이기 때문에 ‘2층 신경망’이라고도 한다.</em></p>
<h1 id="2-퍼셉트론">2. 퍼셉트론</h1>
<p>신경망의 뉴런이 연결되는 방식은 퍼셉트론과 다른 것이 없다.</p>
<h3 id="퍼셉트론">퍼셉트론?</h3>
<p><img src="https://images.velog.io/images/suhyun-guri/post/7f242fa0-f412-4b0b-b19f-ea25815fa709/%ED%8D%BC%EC%85%89%ED%8A%B8%EB%A1%A0.png" alt=""></p>
<h2 id="퍼셉트론이란">퍼셉트론이란?</h2>
<p>퍼셉트론은 신경망(딥러닝)의 기원이 되는 알고리즘이다.</p>
<p>퍼셉트론은 다수의 신호를 입력으로 받아 하나의 신호를 출력한다. (신호는 1과 0으로 이루어져 있다. {1 : 신호가 흐른다, 0 : 신호가 흐르지 않는다})</p>
<p><img src="https://images.velog.io/images/suhyun-guri/post/c364b646-0f68-49c4-ad6e-0f5dd6a059ef/Untitled%20(1).png" alt=""></p>
<p><img src="https://images.velog.io/images/suhyun-guri/post/07c16aa4-debd-445d-be9a-f1dabf819fa8/Untitled%20(2).png" alt=""></p>
<p><code>b</code>는 편향을 나타내는 매개변수로, 뉴런이 얼마나 쉽게 활성화되느냐를 제어한다.</p>
<p><code>w</code>는 각 신호의 가중치를 나타내는 매개변수로, 각 신호의 영향력을 제어한다.</p>
<p>위 그림 퍼셉트론의 동작은 $x_1, x_2$, 1이라는 3개의 신호가 뉴런에 입력되어, 각 신호에 가중치를 곱한 후, 다음 뉴런에 전달된다.</p>
<p>이 신호들의 값을 더하여, 그 합이 0을 넘으면 1을 출력하고 그렇지 않으면 0을 출력한다. 참고로 편향의 입력 신호는 항상 1이다.</p>
<p>위 식을 더 간결한 형태로 다시 작성한다면,</p>
<p>$y = h(b + w_1x_1 + w_2x_2)$  /   $h(x) = \begin{cases}0, &amp;   (x &lt;= 0 )  \1, &amp;   (x &gt; 0) \end{cases}$</p>
<p>입력 신호의 총합이 h(x)라는 함수를 거쳐 변환되어, 그 변환된 값이 y의 출력이 됨을 보여준다.</p>
<h1 id="3-활성화-함수">3. 활성화 함수</h1>
<p>$h(x)$라는 함수 처럼 입력 신호의 총합을 출력 신호로 변환하는 함수를 일반적으로 <strong>활성화 함수</strong>(activation function)라고 한다. 활성화 함수는 입력 신호의 총합이 활성화를 일으키는지를 정하는 역할을 한다.</p>
<p>$a = b+w_1x_1 + w_2x_2$  /  $y = h(a)$</p>
<p>위 식은 가중치가 곱해진 입력 신호의 총합을 계산하고, 그 합을 활성화 함수에 입력해 결과를 내는 2단계로 처리된다.</p>
<p><img src="https://images.velog.io/images/suhyun-guri/post/ccb397c1-4a77-4eed-9531-83c88d36486f/Untitled%20(3).png" alt=""></p>
<p>활성화 함수의 처리 과정을 명시적으로 표현</p>
<p>즉, 가중치 신호를 조합한 결과가 a라는 노드가 되고, 활성화 함수 $h$를 통과하여 y라는 노드로 변환되는 과정이 분명하게 나타나 있다. (뉴런 = 노드)</p>
<p>활성화 함수가 퍼셉트론에서 신경망으로 가기 위한 길잡이이다.</p>
<h2 id="31-활성화-함수-알아보기">3.1 활성화 함수 알아보기</h2>
<h3 id="311-계단-함수">3.1.1 계단 함수</h3>
<p>입력이 0을 넘으면 1을 출력, 그 외에는 0을 출력</p>
<pre><code class="language-python"># 실수만 받아들이는 함수 (배열 X)
def step(x):
    if x &gt; 0:
        return 1
    else:
        return 0

# 넘파이 배열도 지원되도록 수정
def step(x):
    y = x &gt; 0
    return y.astype(np.int)</code></pre>
<p><img src="https://images.velog.io/images/suhyun-guri/post/5d257507-409e-4cd1-8edc-e5eaab932b4f/Untitled%20(4).png" alt=""></p>
<h3 id="312-시그모이드-함수">3.1.2 시그모이드 함수</h3>
<p>시그모이드 함수는 S자형 곡선의 대표 함수이다.</p>
<p><code>e</code>는 자연 상수로 2.7182... 의 값을 가지며, 시그모이드는 계단 함수와 달리 모든 점에서 미분가능하고 모든 점에서 연속인 값을 가진다.</p>
<p>그래프를 그려보면 계단함수처럼 입력이 음수로 들어오면 0으로 수렴하고, 입력이 양수로 들어오면 1로 수렴하는 모습을 가지고 있다. 시그모이드는 계단함수를 대체해서 사용된 활성 함수이다.</p>
<p>$h(x) = { 1 \over {1+e^{-x} } }$</p>
<pre><code class="language-python">def sigmoid(x):
    return 1/(1+np.exp(-x))</code></pre>
<p><img src="https://images.velog.io/images/suhyun-guri/post/f595fbad-6ef3-4956-b6c3-4bd69722559f/Untitled%20(5).png" alt=""></p>
<p>시그모이드는 계단함수를 대체할 좋은 대안이었지만 DNN에서 역전파시에 기울기 소실 문제가 발생한다. DNN은 층을 깊게 해서 학습을 효율을 높이는 방향으로 진행되는데, 층이 깊어지면 깊어질수록 기울기 소실 문제는 더 심해집니다</p>
<p>계단 함수와 시그모이드 함수를 비교했을 때, 가장 먼저 느껴지는 점은 매끄러움의 차이이다.</p>
<p>시그모이드 함수는 부드러운 곡선이며 입력에 따라 출력이 연속적으로 변화한다.</p>
<p>계단 함수는 0을 경계로 출력이 갑자기 바뀌어버린다. 시그모이드 함수의 매끈함이 신경망 학습에서 아주 중요한 역할을 하게 된다.</p>
<p>계단 함수는 0, 1의 값만 반환하는 반면 시그모이드 함수는 실수를 반환하는 점도 다르다.</p>
<p><strong>퍼셉트론에서는 뉴런 사이에 0 또는 1이 흘렀다면, 신경망에서는 연속적인 실수가 흐른다.</strong></p>
<p>두 함수의 공통점으로는 입력이 작을 때의 출력은 0에 가깝고 입력이 커지면 출력이 1에 가까워진다는 구조가 있다. 또한 입력이 아무리 작거나 커도 출력은 언제나 0에서 1 사이이다.</p>
<p>또한 두 함수는 모두 <strong>비선형 함수</strong>이다.</p>
<p>신경망에서는 활성화 함수로 비선형 함수를 사용해야 한다.</p>
<p>선형 함수를 사용하면 신경망의 층을 깊게 하는 의미가 없어진다. 선형 함수의 문제는 층을 아무리 깊게 해도 은닉층이 없는 네트워크로도 똑같은 기능을 할 수 있다는 데 있다. </p>
<p>즉, 층을 쌓는 혜택을 얻고 싶다면 활성화 함수로는 반드시 비선형 함수를 사용해야 한다.</p>
<h3 id="312-relu-함수">3.1.2 ReLU 함수</h3>
<p>ReLU(Rectified Linear Unit) 함수는 입력이 0을 넘으면 그 입력을 그대로 출력하고, 0 이하면 0을 출력하는 함수이다.</p>
<p><img src="https://images.velog.io/images/suhyun-guri/post/ebff3cf0-3d5c-45c6-bf92-c934e33d8f9e/Untitled%20(6).png" alt=""></p>
<p>$relu(x) = \begin{cases}x, &amp;   (x &gt; 0 )  \0, &amp;   (x \le 0) \end{cases}$</p>
<pre><code class="language-python">def relu(x):
    return np.maximum(0, x)</code></pre>
<p>많은 경우 ReLU 함수를 활성 함수로 사용한다.</p>
<p>ReLU는 음이 아닌 구간에서 기울기가 0이 아니기 때문에 시그모이드나 tanh에 비해 빠른 학습이 가능해진다.</p>
<h3 id="313-hyperbolic-tangenttanh">3.1.3 Hyperbolic Tangent(tanh)</h3>
<p>Hyperbolic Tangent(tanh, 하이퍼볼릭탄젠트) 함수는 시그모이드의 대체재로 사용되는 활성 함수이다.</p>
<p>Hyperbolic Tangent 함수는 시그모이드와 유사하고 대신 출력 범위가 -1에서 1로 확장되었다.</p>
<p>tanh 함수의 출력 범위가 시그모이드보다 더 넓고 기울기가 큰 범위가 더 넓기 때문에 시그모이드보다 학습에 유리하다는 장점이 있다.</p>
<p>tanh의 수식은 다음과 같고, 대부분의 프로그래밍 언어와 계산기에 tanh() 함수가 제공되어 사용되고 있다.</p>
<p>$tanh(x) = {{1-e^{-x}} \over {1+e^{-x}}}$</p>
<p><img src="https://images.velog.io/images/suhyun-guri/post/b17a6470-3ad1-44a4-8836-17b7652cc00a/Untitled%20(7).png" alt=""></p>
<p>tanh 함수는 시그모이드보다 출력범위가 넓어졌고, 기울기도 커져서 학습에 유리하지만, 여전히 시그모이드가 가지고 있는 한계를 그대로 가지고 있다. DNN에서의 기울기 소실 문제는 여전히 해결되지 않았다.</p>
<h2 id="32-출력층-활성화-함수">3.2 출력층 활성화 함수</h2>
<p>신경망은 분류와 회귀 모두에 이용할 수 있다. 다만 둘 중 어떤 문제냐에 따라 출력층에서 사용하는 활성화 함수가 달라진다.</p>
<p>일반적으로 회귀에는 항등 함수, 분류에는 소프트맥스 함수를 사용한다.</p>
<h3 id="321-항등-함수identity-function">3.2.1 항등 함수(identity function)</h3>
<p>항등 함수는 입력을 그대로 출력한다. 입력과 출력이 항상 같다.</p>
<p>$f(x) = y$</p>
<h3 id="322-소프트맥스-함수-softmax-function">3.2.2 소프트맥스 함수 (softmax function)</h3>
<p>$y_k = {{{ exp(a_k) }} \over \sum^n_{i=1} exp(a_i)}$</p>
<p>n은 출력층의 뉴런 수, $y_k$는 k번째 출력임을 뜻한다.</p>
<p>소프트맥스 함수의 분자는 입력 신호 $a_k$의 지수 함수, 분모는 모든 입력 신호의 지수 함수의 합으로 구성된다.</p>
<p><img src="https://images.velog.io/images/suhyun-guri/post/e404f6eb-34d6-4edb-96b3-5e0b4f4145ec/Untitled%20(8).png" alt=""></p>
<p>위 그림과 같이 소프트맥스의 출력은 모든 입력 신호로부터 화살표를 받는다. </p>
<p>식의 분모에서 보듯, 출력층의 각 뉴런이 모든 입력 신호에서 영향을 받기 때문이다.</p>
<pre><code class="language-python">def softmax(x):
    exp_a = np.exp(a)
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a
    return y</code></pre>
<h3 id="소프트맥스-함수-구현-시-주의할-점">소프트맥스 함수 구현 시 주의할 점</h3>
<p>앞선 softmax를 컴퓨터로 계산할 때는 오버플로 문제가 생긴다.</p>
<p>softmax는 지수 함수를 사용하는데, 지수 함수라는 것이 쉽게 아주 큰 값을 낸다.</p>
<p>만약 큰 값끼리 나눗셈을 하면 결과 수치가 불안정해진다.</p>
<p>이를 개선하기 위해 수식을 개선해보자.</p>
<p>$y_k = {{{ exp(a_k + C&#39;) }} \over \sum^n_{i=1} exp(a_i + C&#39;)}$</p>
<p>softmax의 지수 함수를 계산할 때 어떤 정수를 더해도 (혹은 빼도) 결과는 바뀌지 않는다.</p>
<p>여기서 $C&#39;$에 어떤 값을 대입해도 상관없지만, 오버플로를 막을 목적으로는 입력 신호 중 최댓값을 이용하는 것이 일반적이다.</p>
<pre><code class="language-python">a = np.array([1010, 1000, 990])
np.exp(a) / np.sum(np.exp(a))
#array([nan, nan, nan])

c = np.max(a)  #1010
a - c
#array([0, -10, -20])

np.exp(a-c) / np.sum(np.exp(a-c))
#array([9.99954600e-01, 4.53978686e-05, 2.06106005e-09])</code></pre>
<p>개선된 python 함수</p>
<pre><code class="language-python">def softmax(a):
    c = np.max(a)
    exp_a = np.exp(a - c) #오버플로 대책
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a
    return y</code></pre>
<pre><code class="language-python">a = np.array([0.3, 2.9, 4.0])
y = softmax(a)
print(y)
# [0.01821127 0.24519181 0.73659691]
np.sum(y)
# 1.0</code></pre>
<p>위와 같이 소프트맥스 함수의 출력은 0에서 1.0 사이의 실수이다. 또한 출력의 총합은 1이다.</p>
<p>출력 총합이 1이 된다는 점은 소프트맥스 함수의 중요한 성질인데 이로 인해 소프트맥스 함수의 출력을 <strong>확률</strong>로 해석할 수 있다.</p>
<h2 id="출처">출처</h2>
<ul>
<li>『밑바닥부터 시작하는 딥러닝』(한빛미디어, 2017)</li>
<li><a href="https://github.com/WegraLee/deep-learning-from-scratch">https://github.com/WegraLee/deep-learning-from-scratch</a></li>
<li>엘리스 AI 트랙</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[엘리스 AI 트랙] 개인 프로젝트 회고]]></title>
            <link>https://velog.io/@suhyun-guri/%EC%97%98%EB%A6%AC%EC%8A%A4-AI-%ED%8A%B8%EB%9E%99-%EA%B0%9C%EC%9D%B8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@suhyun-guri/%EC%97%98%EB%A6%AC%EC%8A%A4-AI-%ED%8A%B8%EB%9E%99-%EA%B0%9C%EC%9D%B8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Thu, 02 Dec 2021 06:18:35 GMT</pubDate>
            <description><![CDATA[<img src = "https://images.velog.io/images/suhyun-guri/post/659f1f34-2b2b-4ff8-af6d-c445b17da75c/%EB%B6%88%ED%86%A0%EB%81%BC.jpg" width="30%" style="margin-left:200px">

<p>엘리스 AI 트랙을 시작한지 벌써 10주차...
9~10주차에는 개인 프로젝트를 진행한다.
프론트엔드, 백엔드 중에 선택할 수 있는데 나는 백엔드를 선택했다.
프론트엔드(React...)는 배우는동안 정말 나랑 안맞는다고 느꼈다....😂
아무튼 주제는 <strong>&quot;도서관 대출 서비스&quot;</strong>이다. 
여태 배웠던 Flask를 사용해 제작하는 프로젝트이다.</p>
<hr>
<h2 id="🐰-프로젝트-소개">🐰 프로젝트 소개</h2>
<p><img src="https://images.velog.io/images/suhyun-guri/post/cff2687f-b8ab-4d03-9a67-5020e23b4332/image.png" alt="">
도서관 대출 서비스 !</p>
<blockquote>
<h3 id="필수-구현-기능">필수 구현 기능</h3>
</blockquote>
<ul>
<li><strong>로그인, 회원가입, 로그아웃, 메인 페이지, 대여하기, 반납하기, 책소개</strong>
이 외 기능들은 선택이다.</li>
</ul>
<h2 id="🐰-프로젝트-진행-과정">🐰 프로젝트 진행 과정</h2>
<p><img src="https://images.velog.io/images/suhyun-guri/post/cf5c0c7d-c332-4bf4-9e85-92d3ba8e098e/image.png" alt=""></p>
<p>실제 프로젝트 기간은 2주이지만, 필수 기능은 1주차에 다 완성하였다.
백엔드 수업을 듣는 동안 코치님과 함께 진행했던 프로젝트로 쉽게 구현할 수 있었다.
그리고 Bootstrap을 사용하여 CSS 적용에도 많은 시간을 절약할 수 있었다.
그 후 선택 기능을 구현한 후, 부가 기능들도 구현했다.</p>
<h2 id="🐰-주요-페이지">🐰 주요 페이지</h2>
<h4 id="로그인-페이지">로그인 페이지</h4>
<p><img src="https://images.velog.io/images/suhyun-guri/post/ba4c6c94-8e06-4998-b2b7-2014f761b79b/image.png" alt=""></p>
<h4 id="회원가입-페이지">회원가입 페이지</h4>
<p><img src="https://images.velog.io/images/suhyun-guri/post/14219218-1bb6-437e-bc13-4009ff57e1d5/image.png" alt=""></p>
<h4 id="메인-페이지">메인 페이지</h4>
<p><img src="https://images.velog.io/images/suhyun-guri/post/71852bcb-4b77-47e1-a694-ac139104f5b3/Animation.gif" alt=""></p>
<h5 id="메인---검색">메인 - 검색</h5>
<p><img src="https://images.velog.io/images/suhyun-guri/post/23282f45-d391-4d8a-b19b-2bd30cead454/Animation1.gif" alt=""></p>
<h4 id="대여--반납">대여 &amp; 반납</h4>
<p><img src="https://images.velog.io/images/suhyun-guri/post/417ed9ad-9c88-4b95-a0d9-402450df531d/%EB%8C%80%EC%97%AC%EB%B0%98%EB%82%A9.gif" alt=""></p>
<h4 id="나의-도서관">나의 도서관</h4>
<p><img src="https://images.velog.io/images/suhyun-guri/post/bdebb380-2e7c-4ca4-98d9-cb6d3a83d371/%EB%82%98%EC%9D%98%EB%8F%84%EC%84%9C%EA%B4%80.gif" alt=""></p>
<h4 id="책-소개-리뷰">책 소개, 리뷰</h4>
<p><img src="https://images.velog.io/images/suhyun-guri/post/dc259825-7181-4bb3-9a74-e3b1aeb01853/%EC%B1%85%EC%9E%90%EC%84%B8%ED%9E%88,%20%EB%A6%AC%EB%B7%B0.gif" alt=""></p>
<h2 id="🐰-데모-페이지">🐰 데모 페이지</h2>
<blockquote>
<p><a href="http://elice-kdt-3rd-vm-059.koreacentral.cloudapp.azure.com/">http://elice-kdt-3rd-vm-059.koreacentral.cloudapp.azure.com/</a>
일주일만 열린다고 한다.. 곧 접속 불가능할 예정</p>
</blockquote>
<h2 id="🐰-어려웠던-점--배운점">🐰 어려웠던 점 &amp; 배운점</h2>
<ul>
<li>이번에 Bootstrap을 적용해보면서 만들어진 CSS를 적용하는 것의 편리함을 느꼈지만, 그 틀에 맞춰 적용을 해야하니 각 클래스 파악에 조금 힘들었고 CSS에 익숙하지 않다보니 어떻게 지정을 해야 내가 생각한 위치에 가는지 모르겠어서 혼란스러웠다.</li>
<li>또한 모달을 만들 때, bootstrap class를 지정하여 구현하였는데 계속 실행도 안되고 에러라고도 안뜨는 상황에서 뭐 때문에 안되는지 혼란스러웠는데, 알고보니 <code>data-toggle</code>, <code>data-target</code> 등 이렇게 되어있는 옵션들이 Bootstrap v5에는 <code>data-bs-toggle</code>, <code>data-bs-target</code>으로 바뀌었다고 한다.. 그래서 실행이 안되었던 것이다.
해결하는데 시간을 오래 들였는데 이러한 간단한 문제였다는 것을 알게되고 굉장히 허무했다</li>
<li>그래도 Bootstrap을 사용해보면서 사용법을 익힐 수 있었던 점이 좋았다.</li>
<li>또한 데이터베이스도 직접 설계해보면서 감을 익힐 수 있었다</li>
<li>여러 라이브러리를 접해보고 활용하는 법을 배웠다.</li>
<li>웹 사이트 제작 프로세스에 대해 이제 조금 알 것 같다.. 그래도 공부를 더더더 많이 해야겠다는 생각을 했다.<img src="https://images.velog.io/images/suhyun-guri/post/149294f3-560a-48cf-8946-14c0bafb44f3/image.png" alt=""></li>
</ul>
<h2 id="🐰-느낀-점">🐰 느낀 점</h2>
<ul>
<li>엘리스에서 기본 코드를 제공해주실 줄 알았는데 완전 백지에서 시작하는 거라고 해서 처음에는 너무 막막했지만, 여태 배웠던 것들을 가지고 기억에서 하나씩 꺼내서 적용하고 바로 실행 결과를 확인하는 이 과정이 너무 재미있었다.</li>
<li>또한 필수 기능들을 다 구현한 후, 내가 한 번 만들어보고 싶은 기능들을 하나하나씩 구현해보고 실제 실행이 되는 것을 보고 신기하고 재미있었으며 더 많은 기능들을 구현해내고 싶다는 욕심이 더 났다.
이것도 하고 싶고 저것도 구현하고 싶고... 그래서 최대한 구현을 하긴 했는데 하지 못한 것에 대한 아쉬움은 남는다.</li>
</ul>
<blockquote>
<p>다음 팀 프로젝트까지 열심히 백엔드 공부하자...!!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Flask] Flask 기초]]></title>
            <link>https://velog.io/@suhyun-guri/Flask-Flask-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@suhyun-guri/Flask-Flask-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Sat, 06 Nov 2021 16:01:19 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-1-flask란">📌 1. Flask란..</h1>
<ul>
<li>파이썬으로 작성된 자유도가 높은 간결한 프레임워크 !</li>
<li>Flask를 이용하면 하나의 파일에 구성된 짧은 코드만으로 완벽하게 동작하는 웹 프로그램을 만들 수 있다.</li>
<li>Flask에는 폼과 데이터베이스를 처리하는 기능이 없다. 대신 확장 모듈을 사용하여 이를 보완한다.</li>
<li>개발자가 필요한 확장 모듈을 그때그때마다 포함해가며 개발하므로 Flask 프로젝트는 가볍고 쉽게 확장이 가능하다.</li>
</ul>
<h2 id="flask-simple-web-server-만들기">Flask Simple Web Server 만들기</h2>
<p>초기 설치 : <code>pip3 install flask</code></p>
<pre><code class="language-py">from flask import Flask
app = Flask(__name__)

@app.route(&#39;/&#39;)
def guri():
    return &quot;hello world&quot;

if __name__ = &quot;__main__&quot;:
    app.run()</code></pre>
<p><code>app = Flask(__name__)</code> : 이 파일이 다른 파일을 통해 실행이 됐는지 아니면 직접 실행된 건지를 알려준다.
<code>@app.route(&#39;/&#39;) ~~</code> : Flask의 기능과 Python의 함수가 합쳐져서 하나의 API 역할을 한다.
<code>@</code> : 데코레이터 =&gt; 어떤 반복적인 기능을 만들기 위해서, 자주 사용하는 기능을 만들기 위해서 코드를 반복해서 사용하는 경우가 있는데 이 데코레이터가 그런 수고를 덜어준다.</p>
<h3 id="json-형식-데이터-나타내기">JSON 형식 데이터 나타내기</h3>
<pre><code class="language-py">from flask import Flask, jsonify
app = Flask(__name__)

@app.route(&#39;/&#39;)
def guri():
    data = {&quot;name&quot; : &quot;guri&quot;}
    return jsonify(data)

if __name__ = &quot;__main__&quot;:
    app.run()</code></pre>
<p>flask 패키지 안에 존재하는 <code>jsonify</code>를 import
data를 json 형식으로 바꿔서 리턴한다.
결과값은 <code>{&quot;name&quot; : &quot;guri&quot;}</code>이 화면에 나타난다.</p>
<h3 id="html-형식-데이터-나타내기">HTML 형식 데이터 나타내기</h3>
<p><img src="https://images.velog.io/images/suhyun-guri/post/f75ddb37-779f-46fc-9fbd-7411a81f9687/image.png" alt="">
HTML을 화면에 전달하기 위해서는 html 파일이 필요하다.
html 파일은 위 사진처럼 templates라는 폴더 아래에 넣어줘야 한다. (<code>index.html</code>)
templates 폴더에 html 파일을 넣어주면 Flask가 자동으로 찾아서 연결해준다 ! </p>
<pre><code class="language-py">from flask import Flask, render_template
app = Flask(__name__)

@app.route(&#39;/&#39;)
def guri():
    return render_template(&#39;index.html&#39;)

if __name__ = &quot;__main__&quot;:
    app.run()</code></pre>
<p>그리고 <code>app.py</code>에서 <code>render_template</code>를 import하고 리턴 값에 <code>render_template(html파일이름)</code>을 준다.
<code>render_template</code>라는 함수는 templates라는 폴더 안의 html 파일을 읽어오는 역할을 한다.</p>
<h3 id="여러-가지-url-연결">여러 가지 Url 연결</h3>
<p><code>@app.route(&#39;URL주소&#39;)</code> 형식으로 여러 개 사용해서 다양한 url을 만들 수 있다.
주의사항은 다른 <code>@app.route</code>에 중복된 이름(앞서 다른 app.route에서 정의한 이름)의 함수를 생성할 수 없다</p>
<pre><code class="language-py">@app.route(&#39;/&#39;)
def guri():
    return jsonify(&quot;hello&quot;)
@app.route(&#39;/list&#39;)
def guri():
    return jsonify(&quot;list page&quot;)
...
</code></pre>
<h1 id="📌-2-rest-api">📌 2. REST API</h1>
<p>REST API ? </p>
<ul>
<li>HTTP URL을 통해 데이터의 자원을 표현하고 HTTP Method(POST, GET, PUT, DELETE)를 통해서 데이터를 다루는 방법을 의미한다. (CRUD Operation 적용)</li>
<li>다양한 클라이언트가 생겨남에 따라서 REST API가 필요하다 - 다양한 곳에서 통신</li>
<li>메시지가 의도하는 바를 URL에서 나타내므로 쉽게 기능을 파악할 수 있다.<h3 id="http-method">HTTP Method</h3>
</li>
<li>GET<ul>
<li>데이터와 URL 뒤에 ?와 함께 사용</li>
<li>https:// <del>~</del> ?name=elice&amp;lecture=python ⇒ GET 방식으로 서버에 data를 전송하는 방법 - 길이 제한 있음. 보안 취약</li>
<li>그래서 로그인의 경우 GET방식 사용 X</li>
</ul>
</li>
<li>POST<ul>
<li>특정 양식(form)에 데이터를 넣어 전송하는 방법</li>
<li>data를 html의 body에 저장해서 서버로 전송 (GET은 header에 추가해서 전송)</li>
<li>다른 사람이 볼 수 없어 보안이 좋고 데이터 길이에 제한이 없다.<pre><code class="language-py">@app.route(&#39;url1&#39;, methods=[&quot;GET&quot;])
@app.route(&#39;url2&#39;, methods=[&quot;POST&quot;])
@app.route(&#39;url3&#39;, methods=[&quot;GET&quot;, &quot;POST&quot;])</code></pre>
methods라는 옵션을 추가해서 해당하는 HTTP Method만 사용할 수 있도록 적용</li>
</ul>
</li>
</ul>
<p><strong>GET</strong></p>
<pre><code class="language-py">from flask import *
app = Flask(__name__)
@app.route(&quot;/&quot; , methods=[&quot;GET&quot;] ) # URL 뒤에 ?name=guri 넣어 GET 요청
def guri():
    name = request.args.get(&#39;name&#39;) 
    result = &quot;hello. &quot; + name
    return result
if __name__== &quot;__main__&quot;:
    app.run()</code></pre>
<p>=&gt; <code>name = request.args.get(&#39;name&#39;)</code> : 주소 (localhost:5000?name=guri)치면 name을 리턴</p>
<p><strong>POST</strong>
templates/index.html 생성해서 그 안에 form을 생성한다.</p>
<pre><code class="language-py">from flask import *
app = Flask(__name__)
@app.route(&quot;/login&quot; , methods=[&quot;POST&quot;] )
def guri_login():
    id = request.form[&#39;id&#39;] 
    password = request.form[&#39;password&#39;] 
    if id == &#39;guri&#39; and password = &#39;1111&#39;:
        return &#39;Hello&#39;
    else:
        &#39;Bye&#39;
if __name__== &quot;__main__&quot;:
    app.run()</code></pre>
<p><code>request.form[~~]</code>을 통해 데이터를 받아옴</p>
<h1 id="📌-3-blueprint-jinja">📌 3. Blueprint, Jinja</h1>
<h4 id="blueprint">Blueprint?</h4>
<ul>
<li>API들을 분류/관리해주는 것</li>
<li>Flask의 기능이 점점 늘어날수록, 코드의 양도 증가한다.</li>
<li>이때, Blueprint를 사용해서 길어진 코드를 모듈화해주어 수정 개발과 유지보수에 용이하게 코드를 관리할 수 있음.</li>
<li>한 파일에서 (<code>app.py</code>) 여러 파일로 생성함 ! </li>
</ul>
<p><strong>Blueprint 사용</strong></p>
<ul>
<li><code>board = Blueprint(&#39;board&#39;, __name__)</code> : 이름 선언</li>
<li>기존 <code>@app.route()</code> 였던 것을 <code>@board.route()</code>로 변경 </li>
<li>파일로 다면 (user_api.py, board_api.py) <code>app.py</code>에서 <code>from board_api import board</code> 이런 식으로 불러와줘야 함</li>
<li>그러고 <code>app.register_blueprint(board)</code>로 Blueprint 등록<pre><code class="language-py">from flask import Blueprint
# 블루프린트 객체 생성
bp = Bluprint(“hello”, __name__, url_prefix=“/hello”)
# app.route 데코레이터가 아닌 bp.route로 선언
@bp.route(‘/’)
def hello_guri():
  return ‘Hello, GURI!”</code></pre>
</li>
</ul>
<h4 id="jinja2">Jinja2</h4>
<ul>
<li>Python에서 가장 많이 사용하는 템플릿</li>
<li>서버에서 받아온 데이터를 효과적으로 보여주고 비교적 간략한 표현으로 데이터를 가공할 수 있다.</li>
<li>간략한 표현 = HTML 내에서 python 문법처럼 데이터를 불러올 수 있다.</li>
<li>반복문, if문 사용 가능 =&gt; 효과적으로 데이터를 나타낼 수 있다.</li>
<li>for문 사용법 : <code>{{% for d in data %}} ~ {{% endfor %}}</code></li>
<li>if문 사용법 : <code>{{% if name == &#39;guri&#39; %}} ~ {{% endif %}}</code></li>
</ul>
<pre><code class="language-py">#if 사용법 (+ for문 )
{% for student in student_list %}
    {% if student[&#39;name&#39;] == &#39;guri&#39; %}
        &lt;p&gt; {{ student[&#39;text&#39;] }} &lt;/p&gt;
    {% endif %}
{% endfor %}</code></pre>
<hr>
<blockquote>
<p><strong>📑 중요 키워드</strong>
<code>@app.route</code> 사용해서 url 생성
<code>jsonify</code>로 json 형식 데이터 전송
<code>render_template</code> 사용해서 HTML 파일 연결
GET은 <code>request.args.get(~)</code>, POST는 <code>request.form[~]</code>
Blueprint로 코드 모듈화! 유지보수 good
jinja 템플릿으로 html 내에서 python문법처럼 데이터 다루기 가능</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BaekJoon/Python] while문 : 10952, 10951, 1110]]></title>
            <link>https://velog.io/@suhyun-guri/BaekJoonPython-while%EB%AC%B8-10952-10951-1110</link>
            <guid>https://velog.io/@suhyun-guri/BaekJoonPython-while%EB%AC%B8-10952-10951-1110</guid>
            <pubDate>Sat, 30 Oct 2021 11:54:28 GMT</pubDate>
            <description><![CDATA[<h1 id="백준-단계-4-while문">백준 단계 4, while문</h1>
<h2 id="10952-ab---5">#10952 A+B - 5</h2>
<blockquote>
<p>두 정수 A와 B를 입력받은 다음, A+B를 출력하는 프로그램을 작성하시오.입력은 여러 개의 테스트 케이스로 이루어져 있다.
각 테스트 케이스는 한 줄로 이루어져 있으며, 각 줄에 A와 B가 주어진다. (0 &lt; A, B &lt; 10)
입력의 마지막에는 0 두 개가 들어온다.</p>
</blockquote>
<h4 id="👩🏻💻-my-code-">👩🏻‍💻 My Code :</h4>
<pre><code class="language-py">while True:
    a, b = map(int, input().split())
    if a == 0 and b == 0:
        break
    print(a+b)</code></pre>
<h2 id="10951-ab---4">#10951 A+B - 4</h2>
<blockquote>
<p>두 정수 A와 B를 입력받은 다음, A+B를 출력하는 프로그램을 작성하시오.
입력은 여러 개의 테스트 케이스로 이루어져 있다.
각 테스트 케이스는 한 줄로 이루어져 있으며, 각 줄에 A와 B가 주어진다. (0 &lt; A, B &lt; 10)
** 위 문제와 다른 점은 입력을 끝내는 신호(?)가 없다. - try, except 사용</p>
</blockquote>
<h4 id="👩🏻💻-my-code--1">👩🏻‍💻 My Code :</h4>
<pre><code class="language-py">#10951
while True:
    try:
        a, b = map(int, input().split())
        print(a+b)
    except:
        break</code></pre>
<ul>
<li>try - except문 <ul>
<li>먼저, try와 except 사이의 문장들이 실행</li>
<li>예외가 발생하지 않으면 except절을 건너뛰고 try문의 실행은 종료</li>
<li>예외 발생시 except절 실행</li>
<li>여기선 break 했으므로 while문 탈출 !</li>
</ul>
</li>
</ul>
<h2 id="1110-더하기-사이클">#1110 더하기 사이클</h2>
<blockquote>
<p>0보다 크거나 같고, 99보다 작거나 같은 정수가 주어질 때 다음과 같은 연산을 할 수 있다.
먼저 주어진 수가 10보다 작다면 앞에 0을 붙여 두 자리 수로 만들고, 각 자리의 숫자를 더한다.
그 다음, 주어진 수의 가장 오른쪽 자리 수와 앞에서 구한 합의 가장 오른쪽 자리 수를 이어 붙이면 새로운 수를 만들 수 있다. 
** 다음 예를 보자.
26부터 시작한다. 2+6 = 8이다. 새로운 수는 68이다. 6+8 = 14이다. 새로운 수는 84이다. 8+4 = 12이다. 새로운 수는 42이다. 4+2 = 6이다. 새로운 수는 26이다.
위의 예는 4번만에 원래 수로 돌아올 수 있다. 따라서 26의 사이클의 길이는 4이다.
N이 주어졌을 때, N의 사이클의 길이를 구하는 프로그램을 작성하시오.</p>
</blockquote>
<h4 id="👩🏻💻-my-code--2">👩🏻‍💻 My Code :</h4>
<pre><code class="language-py">n = int(input())
count = 0
temp = n

while True:
    left = temp // 10
    right = temp % 10
    newright = (left+right) % 10
    temp = (right*10) + newright
    count+=1
    # print(left, right, newright, temp)
    if temp == n:
        break

print(count)
</code></pre>
<hr>
<p>백준 단계 4, while문 끝 !</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[데이터베이스/SQL] SQL 기본 문법]]></title>
            <link>https://velog.io/@suhyun-guri/SQL-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%95-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@suhyun-guri/SQL-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%95-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Sat, 30 Oct 2021 09:13:18 GMT</pubDate>
            <description><![CDATA[<h1 id="✨-sql-명령어-종류">✨ SQL 명령어 종류</h1>
<ul>
<li>DDL (Data Definition Language) - 데이터 정의어<ul>
<li>CREATE, ALTER, DROP, RENAME</li>
</ul>
</li>
<li>DML (Data Manipulation Language) - 데이터 조작어<ul>
<li>SELECT, INSERT, UPDATE, DELETE</li>
</ul>
</li>
<li>DCL (Data Control Language) - 데이터 제어어<ul>
<li>GRANT, REVOKE</li>
</ul>
</li>
<li>TCL (Transaction Control Language) - 트랜젝션 제어어<ul>
<li>COMMIT, ROLLBACK</li>
</ul>
</li>
</ul>
<h2 id="💫-ddl">💫 DDL</h2>
<p>: CREATE, ALTER, DROP, RENAME</p>
<h3 id="🚀-create">🚀 CREATE</h3>
<ul>
<li><p>테이블 생성 시 주의사항 : 반드시 문자로 시작, A-Z, a-z, 0-9, _, $. # 문자만 허용</p>
<pre><code class="language-sql">CREATE TABLE tbname(
id CHAR(7) NOT NULL
name VARCHAR(20) NOT NULL,

PRIMARY KEY (id));
</code></pre>
</li>
</ul>
<pre><code>- 속성의 주요 데이터 타입
  - INT or INTEGER : 정수
  - SMALLINT : INT보다 작은 정수
  - CHAR(n) or CHARACTER(n) : 길이가 n인 고정 길이의 문자열
  - VARCHAR(n) or CHARACTER VARYING(n) : 최대 길이가 n인 가변 길이의 문자열
  - NUMERIC(p,s) : 고정 소수점 실수. p는 소수점 제외 숫자 길이, s는 소수점 이하 숫자 길이
  - FLOAT(n) : 길이가 n인 부동 소수점 실수
  ex. NUMERIC(5,3) = 35.764 / FLOAT(5) = 357.23
  - DATE : 연,월,일로 표현되는 날짜
  - TIME : 시,분,초로 표현되는 시간
  - DATETIME : 날짜와 시간


- 속성 관련 부가 명령어
  - NOT NULL : 해당 속성 값 NULL 불가
  - UNIQUE : 중복 X, 고유값
  - DEFAULT 기본값 : 입력하지 않으면 기본값으로 설정됨
  - CHECK 체크조건 : 체크 조건이 맞는지 확인
```sql
CREATE TABLE tbname(
  id CHAR(7) NOT NULL UNIQUE
  name VARCHAR(20) NOT NULL,
  grade INT NOT NULL DEFAULT 1 CHECK(grade &lt;= 4),

  PRIMARY KEY (id));
</code></pre><h3 id="🚀-alter">🚀 ALTER</h3>
<p>기본 문법 : <code>ALTER TABLE 테이블이름 [ADD, DROP COLUMN, MODIFY 등]</code></p>
<h3 id="🚀-drop">🚀 DROP</h3>
<p>기본 문법 : <code>DROP TABLE 테이블이름</code></p>
<h2 id="💫-dml">💫 DML</h2>
<p>: SELECT, INSERT, UPDATE, DELETE</p>
<h3 id="🚀-select">🚀 SELECT</h3>
<pre><code class="language-sql">SELECT [ALL(*)|DISTINCT] 속성 이름(들)
FROM 테이블 이름(들)
[WHERE 검색조건(들)]
[GROUP BY ... HAVING ... ORDER BY ...]</code></pre>
<ul>
<li><p>검색 SQL의 내부 실행 순서
FROM - WHERE - [GROUP BY - HAVING - ORDER BY] - SELECT</p>
<ul>
<li>SELECT가 가장 마지막에 실행됨 !</li>
</ul>
</li>
<li><p>WHERE절 주요 연산자</p>
<table>
<thead>
<tr>
<th align="center">연산자</th>
<th align="center">예시</th>
</tr>
</thead>
<tbody><tr>
<td align="center">=, &lt;&gt;, &lt;, &lt;=, &gt;, &gt;=</td>
<td align="center">price &lt; 20000</td>
</tr>
<tr>
<td align="center">BETWEEN</td>
<td align="center">price BETWEEN 1000 AND 200000</td>
</tr>
<tr>
<td align="center">IN, NOT IN</td>
<td align="center">price IN (10000, 20000, 300000)</td>
</tr>
<tr>
<td align="center">LIKE</td>
<td align="center">name LIKE &#39;김%&#39; (김으로 시작함)</td>
</tr>
<tr>
<td align="center">IS NULL, IS NIT NULL</td>
<td align="center">name IS NOT NULL</td>
</tr>
<tr>
<td align="center">AND, OR, NOT</td>
<td align="center">(price &lt; 20000) AND (name LIKE &#39;김%&#39;)</td>
</tr>
</tbody></table>
</li>
<li><p>와일드 문자 종류</p>
<table>
<thead>
<tr>
<th align="center">와일드 문자</th>
<th align="center">의미</th>
<th align="center">예시</th>
</tr>
</thead>
<tbody><tr>
<td align="center">+</td>
<td align="center">문자열을 연결</td>
<td align="center">&#39;안녕&#39; + &#39;하세요&#39; : &#39;안녕하세요&#39;</td>
</tr>
<tr>
<td align="center">%</td>
<td align="center">0개 이상의 문자열과 일치</td>
<td align="center">&#39;%구리%&#39; : &#39;구리&#39;를 포함하고 있는 문자열</td>
</tr>
<tr>
<td align="center">[ ]</td>
<td align="center">1개 문자와 일치</td>
<td align="center">&#39;[0-10]%&#39; : 0-10사이 숫자로 시작하는 문자열</td>
</tr>
<tr>
<td align="center">[^]</td>
<td align="center">1개 문자와 불일치</td>
<td align="center">&#39;[^0-10]%&#39; : 0-10사이 숫자로 시작하지 않는 문자열</td>
</tr>
<tr>
<td align="center">_</td>
<td align="center">특정 위치의 1개 문자와 일치</td>
<td align="center">&#39;_리%&#39; : 두 번째 위치에 &#39;리&#39;가 들어가는 문자열</td>
</tr>
</tbody></table>
</li>
<li><p>집계 함수 종류</p>
<table>
<thead>
<tr>
<th align="center">집계 함수</th>
<th align="center">문법</th>
<th align="center">예시</th>
</tr>
</thead>
<tbody><tr>
<td align="center">SUM</td>
<td align="center">SUM([* or DISTINCT] 속성이름)</td>
<td align="center">SUM( * )</td>
</tr>
<tr>
<td align="center">AVG</td>
<td align="center">AVG([* or DISTINCT] 속성이름)</td>
<td align="center">AVG( price )</td>
</tr>
<tr>
<td align="center">COUNT</td>
<td align="center">COUNT({ ( [* or DISTINCT] 속성이름 ) or * } )</td>
<td align="center">COUNT( * )</td>
</tr>
<tr>
<td align="center">MAX</td>
<td align="center">MAX([* or DISTINCT] 속성이름)</td>
<td align="center">MAX( price )</td>
</tr>
<tr>
<td align="center">MIN</td>
<td align="center">MIN([* or DISTINCT] 속성이름)</td>
<td align="center">MIN( price )</td>
</tr>
</tbody></table>
</li>
<li><p>GROUP BY / HAVING</p>
<ul>
<li><code>GROUP BY 속성이름</code> 으로 속성 값에 대해 데이터를 그룹화,
<code>HAVING 검색 조건(들)</code> 으로 그룹화된 데이터에 검색 조건 적용
```sql</li>
</ul>
</li>
<li><ul>
<li>고객 별로 주문 도서의 총 수량을 구하라. 단, 두 권 이상 구매한 고객만 구하라.
SELECT custid, COUNT(<em>) AS 도서수량
FROM Orders
GROUP BY custid
HAVING count(</em>) &gt;= 2;
```</li>
</ul>
</li>
<li><p>ORDER BY</p>
<ul>
<li><code>ORDER BY 속성이름 [ASC (default) | DESC]</code> : 속성 이름에 대해 정렬</li>
<li>ASC는 기본값
```sql</li>
</ul>
</li>
<li><ul>
<li>도서를 이름순으로 검색하라
SELECT *
FROM Book
ORDER BY bookname;
```<h3 id="🚀-insert">🚀 INSERT</h3>
기본 문법 : <code>INSERT INTO 테이블 이름[(속성리스트)] VALUES (값 리스트);</code>
```sql</li>
</ul>
</li>
<li><ul>
<li>book 테이블에 새로운 도서 &#39;구리구리&#39;을 입력하시오. 
INSERT INTO (book_id, bookname, publisher, price)
VALUES (4123, &#39;구리구리&#39;, &#39;구리출판사&#39;, 2000000000);
```<h3 id="🚀-update">🚀 UPDATE</h3>
기본 문법 : <code>UPDATE 테이블 이름 SET 속성이름1 = 값 [, 속성이름2 = 값2 ...] [WHERE 검색조건];</code>
```sql</li>
</ul>
</li>
<li><ul>
<li>book 테이블에서 제목이 &#39;구리구리&#39;인 도서의 가격을 1000원으로 변경하시오
UPDATE book 
SET price = 1000
WHERE bookname = &#39;구리구리&#39;;
```<h3 id="🚀-delete">🚀 DELETE</h3>
기본 문법 : <code>DELETE FROM 테이블 이름 [WHERE 검색조건];</code>
```sql</li>
</ul>
</li>
<li><ul>
<li>book 테이블에서 book_id가 4123인 도서를 삭제하시오.
DELETE FROM book
WHERE book_id = 4123;<pre><code>

</code></pre></li>
</ul>
</li>
</ul>
<hr>
<blockquote>
<p>😋😋 SQL 문법 너무 재밌음
      다음 포스팅은 JOIN에 대해서 할 예정</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조/알고리즘] 4. 트리]]></title>
            <link>https://velog.io/@suhyun-guri/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-4.-%ED%8A%B8%EB%A6%AC</link>
            <guid>https://velog.io/@suhyun-guri/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-4.-%ED%8A%B8%EB%A6%AC</guid>
            <pubDate>Fri, 29 Oct 2021 11:51:02 GMT</pubDate>
            <description><![CDATA[<h3 id="대표적인-자료구조">대표적인 자료구조</h3>
<ul>
<li>선형 구조 : 스택, 큐<ul>
<li>자료가 순서를 가지고 연속됨</li>
</ul>
</li>
<li>비선형 구조 : 트리, 그래프<ul>
<li>선형 구조에 해당하지 않는 자료구조<h1 id="💥-먼저-그래프graph">💥 먼저, 그래프(Graph)</h1>
</li>
</ul>
</li>
<li>트리는 그래프의 특수한 형태 중 하나이다. 즉, 트리는 그래프라고 할 수 있다.</li>
<li>그러므로 그래프부터 무엇인지 살펴보도록 하자.
<img src="https://images.velog.io/images/suhyun-guri/post/fc2a638a-ba12-4a39-88f4-bfc008c1bc55/image.png" alt="Tree by guri"></li>
<li>위 그림은 동그라미와 선으로 이루어져 있다.</li>
<li>여기서 <strong>동그라미는 정점</strong>, <strong>선은 간선</strong>이다.</li>
<li>정점 : 자료, 상태 등 뭔가를 담고 있는 것 (노드라고도 한다)</li>
<li>간선 : 정점 간의 관계를 나타냄</li>
</ul>
<ul>
<li>어떤 정점에서 간선을 통해 다르 정점으로 이동할 수 있다.<ul>
<li>어떤 정점에서 다른 정점으로 이동하기 위해 거치는 모든 정점을 <code>경로</code>라고 한다.</li>
</ul>
</li>
<li>그래프의 간선은 방향이 있을 수도, 없을 수도 있다.<ul>
<li>방향이 있는 간선을 가진 그래프는 유향 그래프라고 한다.</li>
</ul>
</li>
<li>어떤 정점에서 출발해서 자기 자신으로 돌아오는 경로가 있을 수 있다.<ul>
<li>처음 시작한 정점으로 다시 돌아오는 경로를 <code>사이클</code>이라고 한다.</li>
<li>3 - 5 - 6 - 3  : <code>사이클</code></li>
</ul>
</li>
</ul>
<hr>
<h1 id="💥-이제-트리tree">💥 이제, 트리(Tree)</h1>
<ul>
<li>그래프 중 <strong>특별한 성질</strong>을 갖는 그래프를 트리라고 한다.</li>
<li>특별한 성질 ?<ul>
<li>트리의 간선들은 모두 <strong>방향성</strong>을 갖는다.</li>
<li>어떤 정점을 가리키는 정점의 개수는 <strong>최대 1개</strong>이다.</li>
<li>어떤 정점에서 다른 정점으로 이동할 수 있는 <strong>경로는 1개</strong>다.</li>
<li>트리는 <strong>사이클을 갖지 않는다.</strong></li>
</ul>
</li>
<li>트리에서 어떠한 정점도 가리키지 않는 정점을 루트 노드(Root Node)라고 한다.<ul>
<li>가장 위에 있는 노드</li>
<li>루트 노드로부터 다른 정점까지의 거리를 그 정점의 깊이라고 한다.</li>
</ul>
</li>
<li>임의의 정점 A가 다른 정점 B를 가리킬 때, 즉 A -&gt; B<ul>
<li>A는 B의 부모 노드(Parent Node)</li>
<li>B를 A의 자식 노드(Child Node)</li>
</ul>
</li>
<li>가리키는 정점이 없는 정점을 리프 노드(Leaf Node)라고 한다.<ul>
<li>가장 아래에 있는 노드</li>
</ul>
</li>
</ul>
<p>트리는 계층적인 구조로 되어 있는 자료구조이다.
운영체제에서 파일을 분류하기 위해 사용하는 디렉토리가 트리 구조의 대표적인 예시 !</p>
<h3 id="➕-이진-트리">➕ 이진 트리</h3>
<ul>
<li>각 정점들이 자식 노드를 최대 2개까지만 갖는 트리</li>
<li>이진 탐색 트리 등 유용하게 활용되는 트리 중에는 대부분 이진 트리를 응용한 것<h3 id="➕-포화-이진-트리">➕ 포화 이진 트리</h3>
</li>
<li>리프 노드를 제외한 모든 정점이 항상 자식을 2개씩 갖고 있고</li>
<li>모든 리프 노드의 깊이가 동일한 트리</li>
<li>트리의 높이를 <code>h</code>라고 하면, 정점의 개수는 <code>2^h - 1</code>개 이다.</li>
</ul>
<h3 id="➕-완전-이진-트리">➕ 완전 이진 트리</h3>
<ul>
<li>마지막 깊이를 제외하고 모든 정점이 완전히 채워져 있으며, 마지막 깊이의 정점들은 가능한 한 왼쪽에 있는 트리를 가리키는 것</li>
<li>포화 이진 트리에서 마지막 깊이의 정점이 오른쪽에서부터 일부 제거된 트리라고 볼 수 있다.</li>
<li>높이가 h일 때, 정점의 개수는 2^(h-1) 이상 2^h -1 이하이다.</li>
</ul>
<h3 id="➕-정-이진-트리">➕ 정 이진 트리</h3>
<ul>
<li>리프 노드를 제외한 모든 노드들이 두 개의 자식을 갖고 있는 트리이다.</li>
<li>즉, 모든 정점은 0개 또는 2개의 자식 노드를 가진다.</li>
</ul>
<h2 id="🌳♻-트리의-순회">🌳♻ 트리의 순회</h2>
<ul>
<li>트리의 순회란, 트리의 모든 노드를 방문하는 것이다.</li>
<li>트리에 들어있는 자료에 접근하기 위해 순회한다.</li>
<li>배열, 연결 리스트 등 선형 구조는 각 자료가 순서를 갖지만, 비선형 구조(트리 등)는 정해진 순서가 존재하지 않는다.</li>
<li>트리의 모든 노드를 방문하는 순서는 크게 두 가지가 있다. 이는 그래프 순회에도 동일하게 적용된다.
<img src="https://images.velog.io/images/suhyun-guri/post/01e3877b-bcc1-4e0f-b956-8850f830b031/image.png" alt="">
위 트리를 기준으로 순회를 해보겠다.<h3 id="dfs-깊이-우선-탐색">DFS (깊이 우선 탐색)</h3>
</li>
</ul>
<ol>
<li>전위 순회 : root -&gt; left -&gt; right<ul>
<li>1 - 2 - 4 - 5 - 3 - 6 - 7</li>
</ul>
</li>
<li>중위 순회 : left -&gt; root -&gt; right<ul>
<li>4 - 2 - 5 - 1 - 6 - 3 - 7</li>
</ul>
</li>
<li>후위 순회 : left -&gt; right -&gt; root<ul>
<li>4 - 5 - 2 - 6 - 7 - 3 - 1</li>
</ul>
</li>
</ol>
<ul>
<li>DFS는 재귀 호출을 사용하는 알고리즘으로, DFS를 이해하기 위해서는 트리의 재귀적 특성을 이해해야 한다.
<img src="https://images.velog.io/images/suhyun-guri/post/f5847455-22e3-4f9b-bcea-62158ffabb90/image.png" alt=""></li>
<li>*<em>트리의 재귀적 특성 : *</em> 전체 트리를 순회하기 위해, 서브 트리를 순회한다.</li>
<li>순회를 위해 순회한다 -&gt; 재귀 호출</li>
</ul>
<p><strong>DFS방식 구현</strong></p>
<ul>
<li>preorder, inorder, postorder 구현</li>
</ul>
<pre><code class="language-python">
def preorder(tree) :
    # 순회를 한 결과 방문한 노드들을 순서대로 담고 있는 리스트
    # result에 값을 추가한다 = 현재 노드를 방문한다.
    result = []
    result.append(tree.index)

    if tree.left != None:
        result = result + preorder(tree.left)
    if tree.right != None:
        result = result + preorder(tree.right)
    return result

def inorder(tree) :
    result = []
    if tree.left != None:
        result = result + inorder(tree.left)
    result.append(tree.index)
    if tree.right != None:
        result = result + inorder(tree.right)

    return result

def postorder(tree) :
    result = []
    if tree.left != None:
        result = result + postorder(tree.left)
    if tree.right != None:
        result = result + postorder(tree.right)
    result.append(tree.index)


    return result</code></pre>
<h3 id="bfs-너비-우선-탐색">BFS (너비 우선 탐색)</h3>
<p>방문 순서 : 1 - 2 - 3 - 4 - 5 - 6 - 7
BFS는 큐 자료구조를 이용하여 구현한다.
현재 정점과 이웃한 정점일수록 먼저 방문해야 하므로 FIFO 자료구조인 큐를 이용해야 한다.</p>
<p><strong>BFS방식 구현</strong></p>
<pre><code class="language-python">from queue import Queue

def BFS(tree) :
    q = Queue()
    q.put(tree)

    result = []

    #q에 뭔가 들어있다면 계속 반복을 한다. -&gt; 더이상 노드 없을 때 종료
    while len(q.queue) &gt; 0:
        cur = q.get()
        if cur == None :
            continue
        result.append(cur.index)
        q.put(cur.left)
        q.put(cur.right)

    return result</code></pre>
<h3 id="이진-트리-구현">이진 트리 구현</h3>
<pre><code class="language-py">#Tree 클래스는 어떤 트리의 루트 노드에 대한 정보를 갖고 있다.
#루트 노드를 통해 하위 노드에 접근할 수 있으므로 전체 트리에 접근할 수 있음!
class Tree:
    def __init__(self, i, l, r) :
        self.index = i
        self.left = l
        self.right = r

    #재귀적으로 동작한다.
    #새로운 노드가 현재 노드의 자식으로 추가되어야 하는 경우
    # -&gt; 바로 추가
    #그렇지 않다면, 자기 자식중에 새로운 노드를 받을 수 있는 노드를 탐색한다
    # -&gt; 재귀 알고리즘 도입
    def addNode(self, i, l, r) :
        &#39;&#39;&#39;
        트리 내의 정점 i에 대하여 왼쪽자식을 l, 오른쪽 자식을 r로
        설정해주는 함수를 작성하세요.
        &#39;&#39;&#39;
        #루트 노드에 대한 처리
        if self.index == None or self.index == i:
            self.index = i
            self.left = Tree(l, None, None) if l != None else None
            self.right = Tree(r, None, None) if r != None else None

            return True
        else:
            flag = False

            if self.left != None:
                flag = self.left.addNode(i, l, r)
            if flag == False and self.right != None:
                flag = self.right.addNoade(i, l, r)
            return flag</code></pre>
<h2 id="🌳-트리의-활용---이진-탐색-트리">🌳 트리의 활용 - 이진 탐색 트리</h2>
<ul>
<li>컴퓨터에서 트리를 활용하는 예시는 대표적으로 <strong>이진 탐색 트리</strong>가 있다.</li>
<li>정렬된 상태를 유지하는 배열의 추가, 삭제 연산은 <strong>O(N)의 시간 복잡도</strong>를 가진다.</li>
<li>but 정렬된 자료구조에서 사용할 수 있는 탐색 알고리즘인 <strong>이진 탐색을 이용</strong>하면 정렬된 배열 내에서의 자료 탐색을 <strong>O(logn)</strong>만에 수행 가능<ul>
<li>이진 탐색 : 정렬된 배열의 중간 값과 찾는 값을 비교햇 좌측, 우측 다시 탐색하는 알고리즘</li>
</ul>
</li>
<li><strong>이진 탐색 트리는 항상 정렬된 상태를 유지</strong>하는 자료구조이며, 어떤 정점의 왼쪽 서브 트리는 그 정점보다 같거나 작은 정점들로만, 오른쪽 서브 트리는 그 정점의 값보다 큰 정점들로만 이루어져 있다 !</li>
<li>이진 탐색 트리에서 각 요소를 오름차순으로 탐색하기 위해서 중위 순회를 이용할 수 있다.</li>
<li>삽입, 삭제 시간 복잡도는 트리의 높이에 비례</li>
</ul>
<h3 id="트리의-높이--너비">트리의 높이 &amp; 너비</h3>
<ul>
<li><strong>높이</strong></li>
</ul>
<p>트리를 DFS로 순회하다 보면 언젠가 리프 노드에 도달하게 되는데,</p>
<p>이때 각 노드가 루트 노드로부터 얼마나 떨어져 있는지 계산할 수 있다</p>
<p><strong>모든 리프 노드에 대해 깊이를 구하고, 가장 큰 값에 1을 더해 출력해주면 된다.</strong></p>
<ul>
<li>너비</li>
</ul>
<p>주어진 트리의 너비가 가장 큰 레벨과 그 레벨의 너비를 계산해야 한다.
<strong>레벨이란, 깊이가 같은 노드들의 집합을 의미하며 루트 노드부터 1로 시작한다.</strong></p>
<p>같은 레벨의 노드는 같은 행에 위치해야 하고, 한 열에는 하나의 정점만 위치해야 한다.
<img src="https://images.velog.io/images/suhyun-guri/post/11549ffb-ab93-411f-85f1-ca9126bcf467/Untitled.png" alt=""> <em>img출처: 엘리스AI트랙</em></p>
<p>이때 가장 너비가 긴 레벨은 2이고, 그 너비는 4이다.</p>
<p>정점의 행은 각 정점의 깊이를 구하면서 구할 수 있다.</p>
<p>그렇다면 열은 ?</p>
<p>어떤 정점 A의 왼쪽 서브 트리의 정점들의 열이 모두 확정되었다면 비로소 A의 열도 확정지을 수 있다.</p>
<ul>
<li>왼쪽 정점 0 ~ 7 열 차지했다하면, 정점 A는 8번째 열에 넣으면 된다.</li>
<li>오른쪽 정점은 8 ~ n번째에 들어간다</li>
<li>왼쪽 서브 트리를 먼저 확정 짓는다 = 왼쪽 서브 트리를 먼저 방문한다.</li>
</ul>
<p>즉, 중위 순회를 이용하여 트리의 너비를 구할 수 있다.</p>
<p><strong>트리 높이 구하기</strong></p>
<pre><code class="language-python">

def getHeight(myTree) :

    #루트 노드를 포함해서, 왼쪽 서브트리와 오른쪽 서비트리를 모두 포함
    #왼쪽 서브트리의 높이를 구해보고, 
    #오른쪽 서브트리의 높이를 구해보고,
    #두 높이를 비교 =&gt; 더 높은 서브트리의 높이 + 1(루트 노드)

    if myTree == None:
        return 0
    else:
        return 1 + max(getHeight(myTree.left), getHeight(myTree.right))</code></pre>
<p><strong>트리의 너비 구하기</strong></p>
<pre><code class="language-python">

def inorder(tree, depth):
    result = []
    if tree.left != None:
        result += inorder(tree.left, depth+1)

    tree.setDepth(depth)
    result.append(tree)

    if tree.right != None:
        result += inorder(tree.right, depth+1)

    return result

def getWidth(myTree) :

# 반환값 형식 : (l, w)

    result = inorder(myTree, 1)
    # print(&#39;result:&#39;, result)
    n = len(result)

    #정점의 개수는 1000개 이하이다 (입력 조건)
    # 깊이의 최댓값은 1000 이라고 가정
    #left[i] = 깊이가 i인 모든 노드들 중에서, 가장 왼쪽에 있는 노드의 행
    #right[i] = 깊이가 i인 모든 노드들 중에서, 가장 오른쪽에 있는 노드의 행
    #어떤 깊이의 너비는 right[i] - left[i] 인 값
    left = [1001 for i in range(1001)]
    right = [-1 for i in range(1001)]    
    maxDepth = 0

    for i in range(n):
        d = result[i].depth

        left[d] = min(left[d], i)
        right[d] = max(right[d], i)

        maxDepth = max(maxDepth, d)

    ansDepth = 0
    ans = 0

    for i in range(1, maxDepth+1):
        temp = right[i] - left[i] + 1
        if ans &lt; temp:
            ansDepth = i
            ans = temp
    return (ansDepth, ans)*</code></pre>
<hr>
<blockquote>
<p>트리 실습이 은근히 어렵다
  다시 처음부터 차근차근 실습과 함께 공부해보자..!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] React-Router !]]></title>
            <link>https://velog.io/@suhyun-guri/React-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%84%B0</link>
            <guid>https://velog.io/@suhyun-guri/React-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%84%B0</guid>
            <pubDate>Mon, 18 Oct 2021 10:35:09 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-먼저-spa와-mpa">📌 먼저, SPA와 MPA</h1>
<h2 id="spa--single-page-application">SPA ? Single Page Application</h2>
<p>SPA란 말 그대로, 페이지가 하나인 어플리케이션이다. 
하나의 페이지 요청으로 전체 웹앱을 사용하는 방식
전통적인 구조는 MPA(Multi Page Application)</p>
<blockquote>
<p><strong><em>MPA(Multi Page Application) ???</em></strong>
서버에 미리 여러 페이지를 두고, 유저가 네비게이션 시 요청에 적합한 페이지를 전달
미리 서버에서 전체 페이지를 빌드해 브라우저로 전송됨
서버에 라우팅을 처리하는 기능이 있고, 서버에서 여러 페이지를 관리
페이지 요청마다 모든 리소스를 다시 받아오므로, 페이지 간 데이터를 재활용하기 힘들다 !
요즘 웹에서 제공되는 정보가 너무 많기 때문에 MPA는 속도적인 측면에서 문제가 있다.</p>
</blockquote>
<h3 id="spa의-특징">SPA의 특징</h3>
<ul>
<li>Client-side routing 기술을 활용, 페이지 진입 시 리로드없이 라우팅한다.<ul>
<li>e.preventDefault() 실행 - 리로드 없음</li>
</ul>
</li>
<li>AJAX 기술을 활용, 페이지 이동 시 서버에 데이터만 요청하여 자바스크립트로 페이지를 만든다.</li>
<li>MPA와 다르게 여러 페이지를 하나의 앱의 구성요소로 보고 여러 페이지 간의 <strong>스타일, 컴포넌트를 재활용</strong>하는 방향으로 구현</li>
<li>자바스크립트만을 활용해 전체 페이지를 만들기에, 첫 요청 시 빈 페이지를 받게 된다.</li>
</ul>
<p><strong>기술적 장점</strong></p>
<ul>
<li>서버에서 페이지를 만들 필요가 없으므로 CDN에 캐싱이 가능<ul>
<li>서버 성능도 크게 무리 안간다는 장점</li>
<li>CDN : 콘텐츠 전송 네트워크(Content delivery network)</li>
<li>MPA는 CDN 캐싱이 불가능 - 서버에서 빌드를 하기 때문에</li>
</ul>
</li>
<li>매번 페이지 요청을 할 필요가 없어 네트워크 요청이 줄어든다. 마찬가지로 데이터 요청 등을 캐싱하여 재사용하는 등 제약 조건이 줄어든다.</li>
<li>웹사이트를 개별 페이지보다는 하나의 앱으로 보는 설계로 고도의 소프트웨어 설계와 패턴을 적용할 수 있다.<ul>
<li>고도의 소프트웨어 설계 : 상태관리, 라우팅, 컴포넌트 재사용, Hook을 통한 로직 재사용 등</li>
</ul>
</li>
</ul>
<h3 id="spa의-기술적-난관들">SPA의 기술적 난관들</h3>
<ul>
<li>MPA 방식 보다는 Search Engine Optimization(SEO, 검색 엔진 최적화)에 불리함</li>
<li>하나의 자바스크립트 앱이 지속하므로 메모리 관리와 성능, 데이터 활용 등이 중요<ul>
<li>페이지가 리로드 안되므로 한번 페이지가 뜬 상태로 유지가 됨</li>
</ul>
</li>
<li>여러 페이지를 전송받는 것보다 하나의 거대한 자바스크립트 앱을 전송받아야 하므로 코드가 많아질수록 로드 속도가 느려짐</li>
</ul>
<h3 id="spa에서의-라우팅">SPA에서의 라우팅</h3>
<ul>
<li>주로 History API 혹은 URL Hash를 이용해 페이지 리로드 없는 페이지 전환을 구현<ul>
<li>요즘은 History API를 많이 이용</li>
</ul>
</li>
<li>history, location 등 HTML5 API를 활용</li>
<li>visibilitychange, popstate, beforeunload 등 window event를 활용하여 페이지 전환 등의 이벤트 시 핸들러를 등록<ul>
<li>페이지 전환 시 어떠한 처리를 할 수 있도록</li>
</ul>
</li>
<li>react-router, reach-router 등의 라이브러리를 활용하면, 라우팅 관련 기능을 쉽게 사용할 수 있음<ul>
<li>직접 구현 안하고 사용할 수 있음</li>
</ul>
</li>
</ul>
<p>여기까지, SPA, MPA에 대한 이론적인 내용이다.</p>
<hr>
<h1 id="📌-그래서-리액트-라우터">📌 그래서, 리액트 라우터</h1>
<p>여러 개의 페이지로 이루어진 어플리케이션이 있다고 해보자.</p>
<p>상단에 여러 버튼을 누르면 각각 다른 페이지가 나타나고 또 그 안에 다른 페이지가 들어있고 또 ... 페이지가 계속 있을 수 있다.</p>
<p>페이지가 전환될 때마다 주소창의 내용이 달라지며, 각 주소를 방문할 때마다 그에 해당하는 페이지가 열린다.</p>
<p>이를 구현하려면, 어떤 주소로 들어왔을 때 그 주소를 알아내서 그에 해당하는 컴포넌트를 렌더링하고 그 상태를 관리하기 위해 내부적으로 state나 props 같은 값을 복잡하게 사용해야 한다.</p>
<p>이러한 작업은 굉장히 복잡하고 귀찮다. 🤯😩🤯
<strong>이때 이러한 작업을 정말 쉽게 만들어 주는 도구가 바로 <span style="color:red"> React-router-dom </span> 이다 !</strong></p>
<h2 id="react-router">React-router</h2>
<ul>
<li>Declarative routing for React 라는 모토<ul>
<li>Declarative(선언적) : Imperative(명령적)의 반대말</li>
<li>Declarative : <code>&lt;Link to = &quot;/login&quot; /&gt;</code></li>
<li>Imperative : <code>handle(link = () ⇒ push(&#39;/login&#39;))</code></li>
</ul>
</li>
<li>React의 JSX를 이용하거나, History API를 사용하여 라우팅을 구현<ul>
<li>JSX : <code>&lt;Link to = &quot;/login&quot; /&gt;</code></li>
<li>History API : <code>handle(link = () ⇒ push(&#39;/login&#39;))</code></li>
</ul>
</li>
<li>웹에서는 react-router-dom을 사용</li>
<li>적용 시, 서버의 모든 path에서 같은 앱을 서빙하도록 해야 함.<ul>
<li>서버에서 다른 path일 때 다른 앱을 서빙하면 ? MPA<h2 id="react-router의-기능">React-router의 기능</h2>
</li>
</ul>
</li>
<li>React 컴포넌트를 특정 path와 연결하면, 해당 path로 진입 시 해당하는 컴포넌트를 렌더링하게 함</li>
<li>query, path variable 등 URL parameter를 얻어 활용함</li>
<li>조건에 맞지 않을 경우, Redirect</li>
<li>페이지 이동 시, 이벤트 핸들러 등록</li>
</ul>
<h2 id="⭐-react-router의-사용">⭐ React-router의 사용</h2>
<blockquote>
<p><strong>BrowserRouter, Switch, Router, Link 태그 한 줄 내용</strong></p>
</blockquote>
<ul>
<li><code>&lt;BrowserRouter&gt;</code>로 감싸 Router Context를 제공</li>
<li>Switch로 매칭되는 라우트 하나를 (하나만) 렌더링하게 한다.</li>
<li>Route로 path를 정의하고, 그 안에 렌더링하고자 하는 컴포넌트를 넣는다.</li>
<li>Link 태그로 특정 페이지 이동 시, 리로드 없이 페이지가 이동한다.</li>
</ul>
<h3 id="browserrouter">BrowserRouter</h3>
<ul>
<li>HTML5의 History API를 사용하여, UI와 URL의 싱크를 맞추는 역할<ul>
<li>login 버튼을 누르면 <code>test.com/login</code> 이렇게 URL 변경</li>
<li>모든 URL에 대해 동작하게 하기 위해서는 서버 설정 필요</li>
<li>모든 path 앞의 basename을 지정할 수 있음 ex) basename=&quot;/ko&quot;</li>
<li>forceRefresh로 페이지 이동 시 리프레시할 것인지 지정할 수 있음<pre><code class="language-jsx">&lt;BrowserRouter
basename={optionalString}
forceRefresh={optionalBool}
getUserConfirmation={optionalFunc}
keyLength={optionalNumber}
&gt;
&lt;App /&gt;
&lt;/BrowserRouter&gt;</code></pre>
<h3 id="switch">Switch</h3>
</li>
</ul>
</li>
<li>여러 Route 중 매치되는 Route, 위에서부터 <strong>하나</strong> 선택하여 렌더링함</li>
<li>매칭되는 Route가 없으면 아무것도 보여주지 않음<ul>
<li>fallback용으로 404 Not Found Page 추가함</li>
</ul>
</li>
<li><code>path = &quot;/&quot;</code>의 경우, 모든 path에 매칭되므로 <strong>exact 키워드</strong>를 추가하거나 가장 아래로 내림<pre><code class="language-jsx">&lt;Switch&gt;
&lt;Route exact path =&quot;/&quot;&gt;
  &lt;HomePage /&gt;
&lt;/Route&gt;
&lt;Route exact path =&quot;/login&quot;&gt;
  &lt;LoginPage /&gt;
&lt;/Route&gt;
&lt;Route exact path =&quot;/detail&quot;&gt;
  &lt;UserDetailPage /&gt;
&lt;/Route&gt;
&lt;/Switch&gt;</code></pre>
<h3 id="route">Route</h3>
</li>
<li>path와 컴포넌트를 매칭</li>
<li>매칭되는 컴포넌트는 ① children으로 넣어주거나 ② component prop으로 넘김</li>
<li>exact 키워드로 정확하게 매칭되는 path를 설정함.</li>
<li>Route로 렌더링 되는 최상위 컴포넌트는 match, location, history를 prop으로 받음</li>
<li>render prop으로, 매칭되었을 때 실제 어떤 컴포넌트를 렌더링할지 통제함<ul>
<li><code>&lt;Route render = {( ) ⇒ { }}&gt;</code><pre><code class="language-jsx">&lt;Route path=&quot;/&quot;&gt; &lt;Component /&gt; &lt;/Route&gt;</code></pre>
<h3 id="redirect">Redirect</h3>
</li>
</ul>
</li>
<li>Link와 비슷하나, 렌더링되면 to prop으로 지정한 path로 이동함</li>
<li>Switch 안에서 쓰일 경우 from, to를 받아 이동하게 한다<ul>
<li>ex) <code>from = &quot;/&quot; to = &quot;/login&quot;</code><h3 id="link-navlink">Link, NavLink</h3>
</li>
</ul>
</li>
<li>to prop을 특정 URL로 받아, 클릭 시 네비게이션함</li>
<li>anchor tag(a태그)를 래핑<pre><code class="language-jsx">&lt;Link to=&quot;/user&quot;&gt;
  To User
&lt;/Link&gt;</code></pre>
위 코드의 결과 : <code>&lt;a&gt;To User&lt;/a&gt;</code></li>
<li>NavLink의 경우, 매칭 시 어떤 스타일을 가질지 등의 추가 기능이 있음<ul>
<li>Navigation에서 주로 쓰이는 태그</li>
<li><code>&lt;NavLink to &quot;/users&quot;&gt; ... &lt;/...&gt;</code></li>
</ul>
</li>
<li>to에 location object나 함수를 받을 수 있음<ul>
<li>hash, pathname, state 등</li>
<li><code>&lt;Link to = {...}&gt;</code> 가능</li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">&lt;Link to=&quot;/&quot;&gt;home&lt;/Link&gt;
&lt;Link to=&quot;/login&quot;&gt;Login&lt;/Link&gt;</code></pre>
<h2 id="📌-적용-화면">📌 적용 화면</h2>
<p><img src="https://images.velog.io/images/suhyun-guri/post/2ed3a047-92e6-4134-aae4-caad93f01e1a/Animation.gif" alt=""></p>
<ul>
<li>=&gt; <strong>주소창을 보면 리로드없이 주소가 바뀌는 것을 볼 수 있다!</strong></li>
</ul>
<hr>
<h2 id="references">References</h2>
<p><a href="https://react.vlpt.us/react-router/">벨로퍼트의 리액트</a>
<a href="https://book.naver.com/bookdb/book_detail.nhn?bid=18524381">생활코딩! React 리액트 프로그래밍 - 위키북스</a></p>
]]></description>
        </item>
    </channel>
</rss>