<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>ParkNaYeon.log</title>
        <link>https://velog.io/</link>
        <description>Data Science / Computer Vision</description>
        <lastBuildDate>Wed, 01 Jun 2022 13:03:12 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>ParkNaYeon.log</title>
            <url>https://images.velog.io/images/nayeon_p00/profile/8cfd65f0-bb15-4332-ba82-37a0b2b455d0/KakaoTalk_20200829_162604145.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. ParkNaYeon.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/nayeon_p00" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[딥러닝 모델] GAN (Generative Adversarial Network)]]></title>
            <link>https://velog.io/@nayeon_p00/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EB%AA%A8%EB%8D%B8-GAN-Generative-Adversarial-Network</link>
            <guid>https://velog.io/@nayeon_p00/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EB%AA%A8%EB%8D%B8-GAN-Generative-Adversarial-Network</guid>
            <pubDate>Wed, 01 Jun 2022 13:03:12 GMT</pubDate>
            <description><![CDATA[<h2 id="gan의-목적">GAN의 목적</h2>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/d65b156f-d268-4db9-908c-576ad63cb7c7/image.png" alt=""></p>
<p>GAN은 일반적인 머신러닝에서 예측값을 생성해내는 것과 달리, 데이터의 형태를 만들고자 하는 목적을 가지고 있다. 여기서 데이터의 형태는 분포 혹은 분산을 나타내고, 단순히 결과값을 도출하는 함수가 아닌 실제적인 형태를 갖춘 데이터를 만들어 내는 것이다.</p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/f66bdf8d-dfde-43fb-b48b-89cb85327bc8/image.png" alt=""></p>
<p>위 그림은 GAN을 설명할 때 흔히 사용되는 예시이고, 지폐위조범과 경찰이다. 지폐 위조범에게 Generator 라는 역할을, 경찰에게는 Discriminator 역할을 부여한다. 위조지폐범이 위조지폐를 만들었을 때 경찰이 가짜라고 판독하면 위조지폐범은 더 진짜 같은 지폐를 만들게 되고, 이와 같은 과정을 반복하여 진짜 같은 가짜를 생성해내는 능력을 키워주는 아이디어가 GAN의 핵심이다.</p>
<h2 id="gan의-학습-방법">GAN의 학습 방법</h2>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/3114e6d7-1d9c-4b2e-924d-2fdd42c01f09/image.png" alt=""></p>
<p>Generator는 랜덤 노이즈를 생성하는 벡터 z를 input으로 하고, Discriminator가 판별하고자 하는 input 이미지를 output으로 하는 뉴럴 네트워크 유닛이다. 학습과정에서는 실제 이미지를 Discriminator로 하여금 진짜라고 학습시키는 과정과, 벡터 z와 Generator에 의해 생성된 fake 이미지를 가짜라고 학습시키는 두개의 과정으로 나뉜다. 여기서 Discriminator는 이 두번의 과정을 따로 학습하는 것이 아닌, 첫번재 과정에서의 Real image와 Fake image를 Discriminator의 input으로 합쳐서 학습한다.</p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/252cee86-395b-4db0-b31a-0b11871ffd6e/image.png" alt=""></p>
<p>위 수식은 GAN의 목적함수이며, 여기서 V(D,G)는 확률값으로 도출된다. D(Discriminator)의 관점에서 실제 데이터를 입력하면 D(x)가 커지면서 log값이 커져 높은 확률 값이 나오도록 하고, 가짜 데이터(G(z))를 입력하면 log값이 작아져 낮은 확률값이 나오도록 학습된다. D는 실제 데이터와 G(Generator)가 만든 가짜 데이터를 잘 구분하도록 조금씩 업데이트 되는 것이다.</p>
<p>G에서는 노이즈 z를 멀티레이어 퍼셉트론에 통과시켜 샘플들을 생성하며 이 생성된 가짜 데이터 G(z)를 D에 input으로 넣었을 때 실제 데이터처럼 확률이 높게 나오도록 학습된다. 즉 D(G(z))값을 높도록, 전체 확률값이 낮아지도록 하는 것이기 때문에 G가 D를 잘 구분하지 못하는 데이터를 생성하도록 업데이트 된다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[딥러닝 모델] AutoEncoder]]></title>
            <link>https://velog.io/@nayeon_p00/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EB%AA%A8%EB%8D%B8-AutoEncoder</link>
            <guid>https://velog.io/@nayeon_p00/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EB%AA%A8%EB%8D%B8-AutoEncoder</guid>
            <pubDate>Fri, 27 May 2022 14:27:47 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://deepinsight.tistory.com/126">https://deepinsight.tistory.com/126</a></p>
</blockquote>
<h2 id="오토인코더">오토인코더</h2>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/ea531fd4-9078-467b-b9e6-9f176d2fa0a5/image.png" alt=""></p>
<p>오토인코더(Autoencoder)는 단순히 입력을 출력으로 복사하는 신경망이다. </p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/dbe6c1ff-8446-4107-8ce2-1a5b0e9c42fa/image.png" alt=""></p>
<p>이 때 hidden layter의 뉴런수를 input layer보다 작게 해서 데이터를 압축하거나 노이즈를 추가해 원본 입력을 복원할 수 있도록 하는 등의 다양한 오토인코더가 존재한다.</p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/cd2d41f5-87b4-486f-ba8d-3b7397975f8b/image.png" alt=""></p>
<p>수식 </p>
<ul>
<li>Input data를 encoder network에 통과시켜 압축된 z값을 얻는다.</li>
<li>압축된 z vector로부터 Input Data와 같은 크기의 출력 값을 생성한다.</li>
<li>이때 Loss값은 입력값x와 Decoder를 통과한 y값의 차이이다.</li>
</ul>
<p>학습방법</p>
<ul>
<li>Decoder Network를 통과한 output layer의 출력 값은 input값의 크기와 같아야 한다.</li>
<li>학습을 위해서 출력값과 입력값이 같아져야 한다.</li>
</ul>
<p>다시 말해, 입력데이터를 압출시켜 압축 시킨 데이터로 축소한 후에 다시 확장하여 결과 데이터를 입력 데이터와 동일하도록 만드는 과정이며, Unsupervised Learning으로 분류된다.
이때 압출시키는 부분은 Encoder라고 하며, 확장시키는 부분은 Decoder라고 한다.</p>
<h2 id="stacked-auto-encoder">Stacked Auto Encoder</h2>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/f814f441-ee6f-4ea2-8404-ee906bf3920f/image.png" alt=""></p>
<p>AutoEncoder는 보통 Stacked Auto Encoder구조로 많이 활용되며, 대칭적 구조이다.</p>
<p>input layer의 입력데이터를 압축시켜 가장 대표적인 특성을 추출하 층이 Encoded Layer에 있는 노드들이다. 이 층이 압축된 데이터를 갖고 있는 layer이며, 결과 데이터가 입력 데이터와 얼마나 동일하게 출력할 것인가를 결정하는 역할이다. </p>
<h2 id="denoising-auto-encoder-dae">Denoising Auto Encoder (DAE)</h2>
<p>DAE는 임의의 데이터인 노이즈 데이터를 제거하거나, 노이즈를 일부러 추가해 중요한 특징을 추출하도록 하는 Auto Encoder이다. </p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/ac11e8af-d19c-4491-90ca-d9fa99414ff8/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/2dd2103b-0b13-495a-adbe-5a512659f450/image.png" alt=""></p>
<p>가우시안 노이즈를 통해 노이즈를 추가하거나 dropout을 통해 이미지를 예로 든다면 이미지의 픽셀 일부를 사라지게 하는 것이다. &gt; Regularization 효과</p>
<h2 id="variational-auto-encoder-vae">Variational Auto Encoder (VAE)</h2>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/d6f9cd14-7320-4b1c-a0fd-384e4d060c97/image.png" alt=""></p>
<p>노이즈를 추가하는 역할을 주로 하며, hidden layer에서의 노드 값들 중 한번의 샘플링을 통해 대표 특성들의 또 대표 특성들을 추출한다. 여기에 가우시안 노이즈를 더해주어 노이즈가 첨가된 대표특성들을 생성하며, 이를 기반으로 확장하여 출력데이터를 도출하게 된다.</p>
<h3 id="오토인코더의-예시들">오토인코더의 예시들</h3>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/12fd59ae-7280-413f-9152-850ff34423f2/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[딥러닝 모델] 트랜스포머 (Transformer)]]></title>
            <link>https://velog.io/@nayeon_p00/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EB%AA%A8%EB%8D%B8-%ED%8A%B8%EB%9E%9C%EC%8A%A4%ED%8F%AC%EB%A8%B8-Transformer</link>
            <guid>https://velog.io/@nayeon_p00/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EB%AA%A8%EB%8D%B8-%ED%8A%B8%EB%9E%9C%EC%8A%A4%ED%8F%AC%EB%A8%B8-Transformer</guid>
            <pubDate>Sat, 14 May 2022 02:57:13 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://wikidocs.net/31379">https://wikidocs.net/31379</a></p>
</blockquote>
<h2 id="트랜스포머">트랜스포머</h2>
<p>트랜스포머는 RNN을 사용하지 않지만, 기존 seq2seq 처럼 인코더에서 입력시퀀스를 입력받고, 디코더에서 출력시퀀스를 출력하는 인코더-디코더구조를 가진다. 이전에는 하나의 RNN인 t개의 시점을 가지는 구조였는데, 이번에는 인코더와 디코더 단위가 N개로 구성되는 구조이다.</p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/17409b45-4018-4a8d-bb78-64c2b19535fd/image.png" alt=""></p>
<p>위 그림은 인코더와 디코더가 6개씩 존재하는 트랜스포머의 구조이다. 동일한 인코더 레이어와 디코너 레이어가 N번 반복되어 stack 되는 것이다. </p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/69bcfecf-a4a1-4269-b18a-020cd732e859/image.png" alt=""></p>
<p>디코더는 기존 seq2seq 구조처럼 시작 심볼 &lt; sos &gt; 을 입력으로 받아서 종료 심볼 &lt; eos &gt;가 나올 때 까지 연산을 진행한다. 그리고 그 마지막 인코더레이어의 출력을 디코더에 보내게 된다. 단어들이 순차적으로 처리되는 것이 아닌 하나의 레이어에 병렬로 들어간다. </p>
<h2 id="포지셔널-인코딩positional-encoding">포지셔널 인코딩(Positional Encoding)</h2>
<p>트랜스포머에서는 단어입력을 순차적으로 받는 방식이 아니기 때문에 위치정보를 다른 방식으로 알려줄 필요가 있다. 이때 각 단어의 임베딩 벡터에 위치정보들을 더해 모델의 입력으로 사용하게 되고, 이를 포지셔널 인코딩이라고 한다.</p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/b6b8c307-3065-4ab9-beb2-4a3502988322/image.png" alt=""></p>
<p>위 그림에서 알수 있듯, 입력으로 사용되는 임베딩 벡터들이 트랜스포머의 입력으로 사용되기 전에 포지셔널 인코딩값이 더해진다. </p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/561f9f38-107a-4d0e-8ee9-c0dc1c27100c/image.png" alt=""></p>
<p>위 그림처럼 각 단어들의 위치가 반영된 포지셔널 인코딩값이 더해지는 것이다.</p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/281370d1-36d5-4a05-8ff5-73d80f7c5564/image.png" alt=""></p>
<p>트랜스 포머는 위치정보를 가진 값을 만들기 위해서 위 함수를 사용하고, 임베딩 벡터와 포지셔널 인코딩의 덧셈은 임베딩 벡터가 모여서 만들어진 <strong>문장 벡터행렬</strong>과 <strong>포지셔널 인코딩 행렬</strong>의 <strong>덧셈연산</strong>을 통해 이루어진다.</p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/f46f42b5-c686-4f86-88bb-8ceaef97862a/image.png" alt=""></p>
<p>pos는 입력문장에서 임베딩 벡터 위치를 나타내고, i는 임베딩 벡터내 차원의 인덱스를 의미한다. 임베딩 벡터내 각 차원의 인덱스가 짝수인 경우 (pos,2i)사인 함수 값을, 홀수인 경우 (pos,2i+1) 코사인 함수 값을 사용한다.</p>
<h2 id="어텐션attention">어텐션(Attention)</h2>
<p>트랜스포머에서 사용되는 세가지의 어텐션이다.</p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/ca814aba-e560-4424-9699-78b2ec1fb6b0/image.png" alt=""></p>
<p>첫번째 그림은 셀프어텐션으로, 인코더에서 이루어지고, 두번째 그림의 셀프어텐션과 세번째 그림의 인코더-디코더 어텐션은 디코더에서 이루어진다. </p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/3a394479-e63a-401c-9ed2-9e401b64501d/image.png" alt=""></p>
<h2 id="인코더-encoder">인코더 (Encoder)</h2>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/095f57c5-2500-47ff-a644-fa237ba7b81b/image.png" alt=""></p>
<p>트랜스포머는 하이퍼파라미터인 num_layers개수의 인코더 층을 쌓는다. 인코더를 하나의 층이라는 개념으로 생각하면 하나의 인코더 층은 크게 두개의 서브층으로 나뉘고, 이것이 셀프어텐션과 피드 포워드 신경망이다.</p>
<h3 id="인코더의-셀프-어텐션">인코더의 셀프 어텐션</h3>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/7306bb38-fc80-456c-a6dc-c1ea64658f5a/image.png" alt=""></p>
<p>이전에 정리했던 어텐션은 주어진 Query에 대해 모든 Key와의 유사도를 구하고 유사도의 가중치를 각각의 Value에 반영, 이것을 가중합하는 방식이었다. 셀프 어텐션은 이 어텐션을 자기 자신에게 수행한다는 의미인데,</p>
<p><code>Q: 입력 문장의 모든 단어 벡터들</code>
<code>K: 입력 문장의 모든 단어 벡터들</code>
<code>V: 입력 문장의 모든 단어 벡터들</code></p>
<p>셀프 어텐션은 Q,K,V가 모두 동일하다.</p>
<p>셀프어텐션은, 앞에 문장에서 표현한 단어를 뒤에 문장에서 단순히 대명사인 This로 표현했다면, 이를 입력 문장내의 단어들끼리 유사도를 구해주기 때문에 연관성을 찾기 쉬워진다는 장점이 있다.</p>
<p>셀프어텐션은 각 단어 벡터들로부터 Q,K,V벡터를 얻는 작업을 거친다. 이때 Q, K, V 벡터들은 초기 입력인 d model의 차원을 가지는 단어 벡터들보다 더 작은 차원을 가지게 되고, 이 작은 차원은 하이퍼파라미터인 num_heads로 인해 결정되는데, d model을 num_heads로 나눈 값을 각 Q,K,V의 차원으로 결정한다.</p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/d4521371-cc66-4ec6-8263-bd05fcf03c16/image.png" alt=""></p>
<p>위 그림은 student라는 단어 벡터를 Q,K,V벡터로 변환하는 과정이다.</p>
<h3 id="스케일드-닷-프로덕트-어텐션scaled-dot-product-attention">스케일드 닷-프로덕트 어텐션(Scaled dot-product Attention)</h3>
<p>Q, K, V벡터를 얻은 이후에는 기존 어텐션 메커니즘과 동일하다. 각 Q벡터는 모든 K벡터에 대해 어텐션 스코어를 구하고 어텐션 분포를 구한뒤에 이를 V벡터에 가중합하여 어텐션 값을 구하게 된다. 그리고 이 과정을 모든 Q벡터에 대해 반복한다.</p>
<p>어텐션 함수의 종류는 다양하고, 이 중 내적만 사용하는 어텐션 함수가 아닌, 여기에 특정값으로 나눠준 어텐션 함수인 <img src="https://velog.velcdn.com/images/nayeon_p00/post/160083a0-3a01-4bb2-990e-5ed3716c7583/image.png" alt="">
을 사용하는 스케일드 닷-프로덕트 어텐션을 정리해보고자 한다.</p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/34223073-7b10-4be6-94ed-00ce320a8a50/image.png" alt=""></p>
<p>위 그림은 단어 I에 대한 Q벡터가 모든 K벡터에 대해 어텐션 스코어를 구하는 것을 보여준다. I, am, a, student 각 단어와 얼마나 연관되어 있는지를 어텐션 스코어가 나타내고 있고, 두 벡터의 내적값을 스케일링하는 값으로 K벡터의 차원을 나타내는 dk에 루트를 씌운 값을 사용한다. </p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/67c23c62-5a7f-4544-a7a0-d548694cca51/image.png" alt=""></p>
<p>이제 어텐션 스코어에 소프트맥스 함수를 사용해서 어텐션 분포(Attention Distridution)를 구하고, 각 V벡터와 가중합하여 어텐션 값을 구하게 된다. 이것을 단어 I에 대한 어텐션값(context vector)이라고 한다. </p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/71714595-9564-4884-ad9f-745b3860343d/image.png" alt=""></p>
<hr>
<p>0518 추가</p>
<h3 id="멀티-헤드-어텐션-multi-head-attention">멀티 헤드 어텐션 (Multi-head Attention)</h3>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/4ac8dd88-969d-47b5-8731-1e52cb6a99bb/image.png" alt=""></p>
<p>한번의 어텐션을 하는 것보다 여러번의 어텐션을 병렬로 사용하는 것이 더 효과적이라고 판단하여, num_heads의 차원을 가지는 Q, K, V에 대해 num_heads개의 병렬 어텐션을 수행한다. 각 어텐션값 행렬을 어텐션 헤드라고 부르고, 이때 가중치 행렬들의 값은 8개의 어텐션 헤드마다 전부 다르다. 
<img src="https://velog.velcdn.com/images/nayeon_p00/post/a94936b7-7d30-441d-b5e8-ea78b637d0d8/image.png" alt=""></p>
<p>병렬 어텐션을 다 수행했다면 모든 어텐션 헤드를 연결해준다.</p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/3e69d420-e627-4b81-965a-a13faaae9223/image.png" alt=""></p>
<p>어텐션 헤드를 모두 연결한 행렬은 또 다른 가중치 행렬 W0을 곱하게 되는데, 이 후 최종결과가 멀티-헤드 어텐션의 최종 결과물이 된다. </p>
<h3 id="포지션-와이즈-피드-포워드-신경망position-wise-ffnn">포지션-와이즈 피드 포워드 신경망(Position-wise FFNN)</h3>
<p>포지션-와이즈 FFNN은 인코더와 디코더에서 공통적으로 가지는 서브층이다. 포지션-와이즈 FFNN은 Fully - connected FFNN이라고 해석할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/d42d7e6b-c29b-4519-b848-3f8db1e956ff/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/c8503386-e36a-48ac-9ce1-de7dc1f76cde/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/20a678c7-3fea-4381-82aa-465fb4ba8258/image.png" alt=""></p>
<h3 id="잔차-연결residual-connection과-층-정규화layer-normalization">잔차 연결(Residual connection)과 층 정규화(Layer Normalization)</h3>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/2862c952-8cc4-47de-bc22-47ed615baab1/image.png" alt=""></p>
<p>트랜스포머에서는 이러한 두개의 서브층을 가진 인코더에 추가적으로 Add&amp;Norm 기법을 사용한다. </p>
<h4 id="잔차연결">잔차연결</h4>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/38c427bc-0b62-4a08-962e-881ecc03febb/image.png" alt=""></p>
<p>잔차연결은 서브층의 입력과 출력을 더하는 것으로, 모델의 학습을 돕기 위한 기법이다.</p>
<h4 id="층-정규화-layer-normalization">층 정규화 (Layer Normalization)</h4>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/59ae1d84-34d6-44ed-a80a-2385d5eca018/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/e2882489-488b-46d2-b5ea-dbc488b40347/image.png" alt=""></p>
<p>층 정규화는 텐서의 마지막 차원에 대해 평균과 분산을 구하고, 이를 가지고 특정수식을 통해 값을 정규화하여 학습을 돕는다. </p>
<h2 id="인코더에서-디코더로">인코더에서 디코더로</h2>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/1026bd9e-7b60-481b-a6a8-d7e8174e3c43/image.png" alt=""></p>
<p>지금까지는 인코더를 살펴보았고, 이제 디코더 연산이 시작되어 디코더 또한 총 num_layers 만큼의 연산을 하게 된다. 이때 마다 인코더가 보낸 출력을 각 디코더 층 연산에 사용한다.</p>
<h3 id="디코더의-첫번째-서브층--셀프-어텐션과-룩-어헤드-어텐션">디코더의 첫번째 서브층 : 셀프 어텐션과 룩-어헤드 어텐션</h3>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/0e7981be-7762-4adb-92b8-2aacec6a2025/image.png" alt=""></p>
<p>RNN 계열의 신경망은 입력단어를 매 시점마다 순차적으로 받으므로 <strong>다음 단어 예측에 현재 시점 이전에 입력된 단어들만</strong> 참고할 수 있다. 그러나 트랜스포머는 문장단위로 입력을 한번에 받기 때문에 현재 시점보다 미래에 있는 단어들을 참고하지 못하도록 룩-어헤드 마스크를 도입했다.</p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/ccf5b2a8-11e9-472f-be16-e1bab436d96d/image.png" alt=""></p>
<p>룩-어헤드 마스크는 디코더의 첫번째 서브층에서 이루어진다. </p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/4701f7e5-3c6b-4f41-aff4-219eee53e6ec/image.png" alt=""></p>
<p>위 그림처럼 우선 셀프어텐션을 통해 어텐션 스코어 행렬을 얻는다. </p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/5d0a17d0-e513-49b6-b6ee-c33bb249fd6c/image.png" alt=""></p>
<p>그리고 자기 자신보다 미래의 단어들을 제외하여 마스킹한다.</p>
<h3 id="디코더의-두번째-서브층--인코더-디코더-어텐션">디코더의 두번째 서브층 : 인코더-디코더 어텐션</h3>
<p>디코더의 두번째 서브층을 살펴보면, 멀티헤드 어텐션을 수행한다는 점에서 이전의 어텐션들과 같지만 셀프 어텐션은 아니다.</p>
<p><code>인코더의 첫번째 서브층 : Query = Key = Value</code>
<code>디코더의 첫번째 서브층 : Query = Key = Value</code>
<code>디코더의 두번째 서브층 : Query : 디코더 행렬 / Key = Value : 인코더 행렬</code></p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/8274a2e0-69d0-4823-978b-2c96016e3d07/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/ac998878-ab39-466b-a73f-e6e5390466b6/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[딥러닝 모델] 어텐션(Attention)]]></title>
            <link>https://velog.io/@nayeon_p00/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EB%AA%A8%EB%8D%B8-%EC%96%B4%ED%85%90%EC%85%98Attention-z1z9c3eb</link>
            <guid>https://velog.io/@nayeon_p00/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EB%AA%A8%EB%8D%B8-%EC%96%B4%ED%85%90%EC%85%98Attention-z1z9c3eb</guid>
            <pubDate>Sun, 08 May 2022 13:59:19 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://wikidocs.net/22893">https://wikidocs.net/22893</a></p>
</blockquote>
<h2 id="어텐션-등장의-배경">어텐션 등장의 배경</h2>
<p>RNN을 기반으로한 언어 모델에서 크게 두가지 문제가 발생했는데, 먼저 하나의 고정된 크기 벡터에 모든 정보를 압축하려고 하니 정보손실이 발생한다는 점과, 기울기 소실(Vanishing Gradient)문제가 발생한다는 것이다. 이를 해결하기 위해 어텐션이 등장했다.</p>
<p>어텐션은 디코더에서 출력단어를 예측하는 매시점마다, 인코더에서의 전체입력문장을 다시 한번 참고한다. 이때 현재 예측해야할 단어와 연관이 있는 입력단어 부분을 좀 더 집중해서 보게 된다.</p>
<h2 id="어텐션-함수">어텐션 함수</h2>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/341ab7e3-e166-4a23-9d57-1c6a90ab2d54/image.png" alt=""></p>
<p><code>Attention(Q, K, V) = Attention Value</code></p>
<p>어텐션을 함수로 표현하면 위와 같은데, 먼저 주어진 &#39;쿼리&#39;에 대래서 모든 &#39;키&#39;와의 유사도를 각각 구한다. 구한 유사도를 키와 맵핑되어 있는 각각의 &#39;값&#39;에 반영해준다. 그리고 유사도가 반영된 &#39;값&#39;을 모두 더해서 리턴한다.(어텐션 값)</p>
<p><code>Q = Query : t 시점의 디코더 셀에서의 은닉 상태</code>
<code>K = Keys : 모든 시점의 인코더 셀의 은닉 상태들</code>
<code>V = Values : 모든 시점의 인코더 셀의 은닉 상태들</code></p>
<h2 id="닷-프로덕트-어텐션dot-product-attention">닷-프로덕트 어텐션(Dot-Product Attention)</h2>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/9474d966-8a49-4501-bdfb-712656658698/image.png" alt=""></p>
<p>위 그림은 디코더의 세번재 LSTM셀에서 출력 단어를 예측하고자 할때 어텐션을 사용하는 모습이다. 인코더의 소프트맥스 함수를 통해 각 단어들이 출력 단어를 예측할때 얼마나 도움이 되는지를 수치화 한 값을 도출하게 된다. 그림에서는 빨간 직사각형의 크기로 소프트맥스 함수의 결과값의 크기를 표현하고 있다. 이렇게 각 입력단어가 디코더의 예측에 도움이 되는 정도가 수치화하여 측정되면 이것을 하나의 정보로 담아서 디코더로 전송하게 된다. 위 그림에서는 초록색 삼각형이다. </p>
<h3 id="1-어텐션-스코어attention-score-구하기">1. 어텐션 스코어(Attention Score) 구하기</h3>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/5a00a56d-659d-4792-a046-f4fc2f89415d/image.png" alt=""></p>
<p>인코더의 은닉상태(hidden state)를 각각 h1, h2, ... hn이라 하고, 디코더의 현재 시점 t에서의 은닉상태(hidden state)를 st라고 한다. 그리고 t번째 단어를 예측하기 위한 어텐션 값을 at라 정의한다. 이 at를 구하기 위해서 먼저 어텐션 스코어를 구하게 되는데, 어텐션 스코어란 현재 디코더의 시점 t에서 단어를 예측하기 위해, <strong>인코더의 모든 은닉 상태 각각</strong>이 <strong>디코더의 현 시점의 은닉상태 st</strong>와 얼마나 유사한지를 판단하는 스코어값이다.</p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/c89d41cc-deb6-495e-944d-a78ead121310/image.png" alt=""></p>
<p>닷-프로덕트 어텐션에서는 이 스코어값을 구하기 위해서 st를 전치하고 각 은닉 상태와 내적을 수행한다. </p>
<blockquote>
<h4 id="어텐션-스코어-함수">어텐션 스코어 함수</h4>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/7645ef66-9007-4c9d-83be-32c13b5121cc/image.png" alt=""></p>
</blockquote>
<blockquote>
<h4 id="st와-인코더의-모든-hidden-state의-모음값-et">st와 인코더의 모든 hidden state의 모음값 et</h4>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/32f33be6-27b1-48ff-90ec-e49c55f6eaa1/image.png" alt=""></p>
</blockquote>
<br/>

<h3 id="2-소프트맥스softmax함수를-통해-어텐션-분포를-구하기">2. 소프트맥스(softmax)함수를 통해 어텐션 분포를 구하기</h3>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/482f1581-e035-4dd8-ad6f-9d27e0abc22e/image.png" alt=""></p>
<p>et에 소프트맥스 함수를 적용해서, 모든 값을 합하면 1이 되는 확률 분포를 얻어낸다. 이를 어텐션 분포라고 하고, 각각의 값은 어텐션 가중치하고 한다. </p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/730ed94d-487c-452d-921a-90c697a1e842/image.png" alt=""></p>
<p>어텐션 분포를 알파 t 라고 할때 이것을 식으로 정의한 것이다.</p>
<br/>

<h3 id="3-각-인코더의-어텐션-가중치와-hidden-state를-가중합하여-어텐션값attention-value-구하기">3. 각 인코더의 어텐션 가중치와 hidden state를 가중합하여 어텐션값(Attention Value) 구하기</h3>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/478f5744-2903-446c-8dec-1ba85b9449e8/image.png" alt=""></p>
<p>지금까지 준비한 정보들을 하나로 합치는 단계이다. 각 인코더의 hidden state와 어텐션 가중치들을 곱하고 최종적으로 더하는 과정이며 weighted sum을 진행하는 것이다. </p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/82e02b93-b90b-4c70-ac7c-c954c7a016b8/image.png" alt=""></p>
<p>이 어텐션 값은 종종 인코더의 문맥을 포함하고 있따고 해서 context vetor라고도 한다.</p>
<br/>

<h3 id="4-어텐션-값과-디코더의-t시점의-은닉상태-연결하기concatenate">4. 어텐션 값과 디코더의 t시점의 은닉상태 연결하기(concatenate)</h3>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/a3ae4e19-988f-4035-928e-1ac3f39b4471/image.png" alt=""></p>
<p>최종적으로 구한 어텐션값 at를 st와 결합하여 하나의 벡터로 만드는 작업을 수행한다. 이를 vt라고 정의하고, 이 vt를 yhat 예측연산의 입력으로 사용함으로써 인코더로부터 얻은 정보를 활용하여 더 잘 예측할 수 있게 된다.</p>
<h3 id="5-출력층-연산의-입력이-되는-st틸데기호를-계산하기">5. 출력층 연산의 입력이 되는 s~t(틸데기호)를 계산하기</h3>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/363247e3-296d-4f51-af76-6418dd11a0a0/image.png" alt=""></p>
<p>vt를 바로 출력층으로 보내기 전에 신경망 연산을 한번더 추가했다. 가중치 행렬과 곱한 후 에 하이퍼볼릭탄젠트 함수를 지나도록 하여 출력층 연산을 위한 새로운 벡터인 st를 얻는다. </p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/c1b11f3a-f2f9-41ee-a930-2397700eff68/image.png" alt=""></p>
<h3 id="6-st틸데기호를-출력층의-입력으로-사용하기">6. s~t(틸데기호)를 출력층의 입력으로 사용하기</h3>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/d802ba8a-6946-4b0c-8d90-eae940ae5b68/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[딥러닝 모델] RNN 과 LSTM]]></title>
            <link>https://velog.io/@nayeon_p00/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EB%AA%A8%EB%8D%B8-RNN-%EA%B3%BC-LSTM</link>
            <guid>https://velog.io/@nayeon_p00/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EB%AA%A8%EB%8D%B8-RNN-%EA%B3%BC-LSTM</guid>
            <pubDate>Sun, 01 May 2022 09:41:06 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://wikidocs.net/22886">https://wikidocs.net/22886</a>
<a href="https://ratsgo.github.io/natural%20language%20processing/2017/03/09/rnnlstm/">https://ratsgo.github.io/natural%20language%20processing/2017/03/09/rnnlstm/</a></p>
</blockquote>
<h2 id="순환-신경망rnn-recurrent-neural-network">순환 신경망(RNN, Recurrent Neural Network)</h2>
<p>RNN은 입력과 출력을 시퀀스 단위로 처리하는 시퀀스 모델이다. 예를 들어 번역기와 같이 번역하고자 하는 단어의 시퀀스인 문장을 넣어 번역한 문장을 도출하는 시퀀스 등을 처리하기 위한 모델이 시퀀스 모델이다. RNN 중 대표적으로 LSTM 모델이 있으며, 해당 모델을 함께 정리해볼 예정이다. </p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/227a019f-45b9-4844-9675-a9a57cce2a6f/image.png" alt=""></p>
<p>RNN은 히든 노드가 방향을 가진 엣지로 연결돼서 순환구조를 이루는 인공신경망의 한 종류이다. 위 그림은 RNN의 기본 구조를 나타내며, 녹색박스는 히든 state, 빨간박스는 인풋 x, 파란박스는 아웃풋 y이다. 현재상태의 히든 state ht는 직전 시점의 히든 state ht-1를 받아서 갱신된다. 그리고 현재 상태의 아웃풋 yt는 ht를 전달받아 갱신되는 구조이다. 또한 히든 state의 활성화함수는 하이퍼볼릭탄젠트를 사용한다. </p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/98ed5f26-5946-4398-b788-4b7ee4bb7955/image.png" alt=""></p>
<p>위 그림을 예시로 들어 구조를 설명하면, hell 다음 o라는 글자를 예측하고 싶은 상황이다. 이때 각 알파벳들은 원핫벡터로 표현하여 입력레이어로 들어가게 되는데, 먼저 x1인 (1,0,0,0)을 기반으로 h1인 (0.3, -0.1, 0.9)를 만들게 된다. 이것을 바탕으로 y1인 (1.0, 2.2, -3.0, 4.1)을 생성한다. 이 과정으로 2, 3, 4번째 단계들도 모두 갱신하게 되는데, 이 과정을 순전파라고 한다. </p>
<p>이때 다음 글자를 예측하고자 하므로 h 다음 정답은 e, e 다음정답은 l 이런식으로 정답에 해당하는 인덱스 (그림에서 output layer에 초록색으로 표시된 각각의 숫자들) 를 기준으로 역전파를 진행할 수 있다. </p>
<h2 id="rnn-수식-정리">RNN 수식 정리</h2>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/df63fc0d-a4e3-408e-b486-757aba41a281/image.png" alt=""></p>
<p>현재 시점 t에서의 히든state값을 ht라고 정의하고, 히든레이어의 메모리셀은 ht를 계산하기 위해 총 두개의 가중치를 가진다. 하나는 입력층을 위한 가중치 Wx 이고, 하나는 이전시점 t-1의 히든state 값인 ht-1을 위한 가중치 Wh이다. </p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/60e7a3d0-ed3d-4ddd-b783-9b807fed0552/image.png" alt=""></p>
<p>RNN의 은닉층 연산을 벡터와 행렬 연산으로 이해할 수 있다. 입력 x1의 차원을 d라고 하고, 히든 state의 크기를 Dh라 했을 때 각 벡터와 행렬의 크기는 아래와 같다. </p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/b04197a6-e777-45d2-b49e-a53467892198/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/f9c2d46a-df5f-417c-a247-fa923c8d1f57/image.png" alt=""></p>
<p>그리고 위 그림은 d와 Dh값 모두 4로 가정하고 표현한 히든레이어 연산을 그림으로 표현한 모습이다.</p>
<hr>
<h2 id="lstm-long-short-term-memory-models">LSTM (Long Short-Term Memory models)</h2>
<p>RNN은 관련정보와 그 정보를 사용하는 지점 사이 거리가 멀 경우에 역전파시 그래디언트가 점점 줄어 학습능력이 크게 저하되는 것으로 알려져 있다. 이 문제를 해결하기 위해 고안된 것이 LSTM이다. LSTM은 RNN의 히든 state에 cell-state를 추가한 구조이다. </p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/b38d0650-0ef1-4fa1-a429-d2e1a7ab9f8b/image.png" alt=""></p>
<p>cell state는 일종의 컨베이어 벨트 역할을 한다. 덕분에 state가 꽤 오래 경과하더라도 그래디언트가 비교적 전파가 잘 되게 된다. </p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/dfe5083b-af3f-4940-82b5-0b47887971b5/image.png" alt=""></p>
<p>LSTM 셀의 수식을 하나씩 작성한 사진이며,</p>
<p>forget gate ft는 &#39;과거정보를 잊기&#39;를 위한 게이트이다. ht-1과 xt를 받아서 시그모이드 취해준 값이 바로 forget gate가 내보내는 값이 된다. </p>
<p>input gate it⊙gt 는 &#39;현재정보를 기억하기&#39;위한 게이트이다. ht-1과 xt를 받아서 시그모이드를 취하고 또 같은 입력으로 tanh를 취해준 다음 hadamard product연산을 한 값이 바로 input gate가 내보내는 값이 된다. (hadamard product: 같은 크기의 두 행렬의 각 성분을 곱하는 연산)</p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/6a04ea29-4a04-4a40-946f-9cdb0b5152da/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/1551fc4b-5e34-47b6-9183-0d884b0d6a05/image.png" alt=""></p>
<h2 id="단계별-lstm-구조">단계별 LSTM 구조</h2>
<h3 id="forget-gate-layer">forget gate layer</h3>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/17d8b3c7-dc2e-4062-ae11-3295951bd9cd/image.png" alt=""></p>
<p>먼저 앞에서 수식과 함께 살펴봤던 forget gate layer이다. cell state로부터 어떤 정보를 버릴 것인지 정하는 것으로, sigmoid layer에 의해 결정된다. 값이 1이라면 모든 정보를 보존, 0이라면 모든 정보를 버리라는 뜻이다. </p>
<h3 id="input-gate-layer">input gate layer</h3>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/1f216a1e-baee-4397-91ce-23b0f7552d22/image.png" alt=""></p>
<p>다음은 input gate layer이다. 앞으로 들어오는 새로운 정보들 중에 어떤 것을 cell state에 저장할 것인지를 정하는 것이다. 먼저 input gate layer라고 불리는 시그모이드 layer가 어떤 값을 업데이트할지 정하고, 그다음 tanh layer가 새로운 후보 값들인 Ct라는 벡터를 만들어 cell state에 더할 준비를 한다. </p>
<h3 id="cell-state-업데이트">cell state 업데이트</h3>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/c8c235d3-267a-44e1-9e7c-d9f93483df3a/image.png" alt=""></p>
<p>과거 state인 Ct-1를 업데이트해서 새로운 cell state인 Ct를 만들게 된다. 이때 forget gate에서 잊어버린 것들은 잊어버리고, it * Ct를 더하게 된다. 이 더하는 값이 앞서 업데이트 한 값을 얼마나 업데이트 할지 정한만큼 scale한 값이다. </p>
<h3 id="output-gate-layer">output gate layer</h3>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/8cb44574-3d12-48e5-b895-f4bb1fdc77b7/image.png" alt=""></p>
<p>마지막으로, 무엇을 output 으로 내보낼지 정하게 되는데, 먼저 시그모이드 layer에 input데이터를 태워서 cell state의 어떤 부분을 output으로 내보낼지를 정한다. 그 후 cell state를 tanh layer에 태워서 -1과 1사이의 값을 받은 뒤에 sigmoid gate의 output과 곱해준다. 이렇게 해서 output으로 보내고자 하는 부분만 내보내게 된다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[맞춤형 취약품사 보완 추천 서비스(Duolingo SLAM Data)를 위한 모델 개발 | Knowledge Tracing | Ch.1 데이터 수집 및 전처리]]></title>
            <link>https://velog.io/@nayeon_p00/%EB%A7%9E%EC%B6%A4%ED%98%95-%EC%B7%A8%EC%95%BD%ED%92%88%EC%82%AC-%EB%B3%B4%EC%99%84-%EC%B6%94%EC%B2%9C-%EC%84%9C%EB%B9%84%EC%8A%A4Duolingo-SLAM-Data-Knowledge-Tracing-Ch.1-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A7%91-%EB%B0%8F-%EC%A0%84%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@nayeon_p00/%EB%A7%9E%EC%B6%A4%ED%98%95-%EC%B7%A8%EC%95%BD%ED%92%88%EC%82%AC-%EB%B3%B4%EC%99%84-%EC%B6%94%EC%B2%9C-%EC%84%9C%EB%B9%84%EC%8A%A4Duolingo-SLAM-Data-Knowledge-Tracing-Ch.1-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A7%91-%EB%B0%8F-%EC%A0%84%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Sun, 24 Apr 2022 07:47:26 GMT</pubDate>
            <description><![CDATA[<p><a href="http://sharedtask.duolingo.com/2018.html">http://sharedtask.duolingo.com/2018.html</a></p>
<h2 id="데이터-수집">데이터 수집</h2>
<blockquote>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/ee9cd1e7-e32c-45cb-9672-4c628f708116/image.png" alt="">
<a href="https://dataverse.harvard.edu/dataset.xhtml?persistentId=doi:10.7910/DVN/8SWHNO">Datasets Download Link</a></p>
</blockquote>
<p>수집된 데이터는 <a href="http://sharedtask.duolingo.com/2018.html">http://sharedtask.duolingo.com/2018.html</a> 2018 Duolingo Shared Task on Second Language Acquisition Modeling (SLAM) 를 위해 제공된 데이터셋을 활용하였습니다. 그 중 영어를 대상으로 서비스화 하기 위해, en_es — English learners (who already speak Spanish) 데이터를 사용했으며, 스페인어를 구사하고 있는 사용자들의 영어 연습을 담은 데이터 입니다. 해당 데이터는 Duolingo 앱 서비스에서 누적된 사용자들의 토큰별 정오답여부를 담은 데이터로, 데이터 포맷은 아래와 같습니다.</p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/ad8c38b1-ce53-42fd-b19e-380f4dc9f95f/image.png" alt=""></p>
<p><code>user</code> a B64 encoded, 8-digit, anonymized, unique identifier for each student (may include / or + characters) / 사용자 아이디
<code>countries</code> a pipe (|) delimited list of 2-character country codes from which this user has done exercises / 사용자 국가 
<code>days</code> the number of days since the student started learning this language on Duolingo / 사용자가 언어 공부를 하기 시작한 후 지난 일수
<code>client</code> the student&#39;s device platform (one of: android, ios, or web) / 사용 플랫폼
<code>session</code> the session type (one of: lesson, practice, or test; explanation below) / 세션 타입
<code>format</code> the exercise format (one of: reverse_translate, reverse_tap, or listen; see figures above) / 연습 포맷(번역, 단어나열, 듣기)
<code>time</code> the amount of time (in seconds) it took for the student to construct and submit their whole answer (note: for some exercises, this can be null due to data logging issues) / 해당 문장을 공부하는 데 걸린 시간
<code>A Unique 12-digit ID for each token instance</code> the first 8 digits are a B64-encoded ID representing the session, the next 2 digits denote the index of this exercise within the session, and the last 2 digits denote the index of the token (word) in this exercise / 토큰(단어)별 id
<code>The token (word)</code> / 단어
<code>Part of speech in Universal Dependencies (UD) format</code> / 품사
<code>Morphological features in UD format</code>
<code>Dependency edge label in UD format</code>
<code>Dependency edge head in UD format</code> (this corresponds to the last 1-2 digits of the ID in the first column)
<code>The label to be predicted (0 or 1)</code> / 정오답</p>
<hr>
<h2 id="데이터-전처리">데이터 전처리</h2>
<p>이 후 Ch2 에서 사용하게될 DKT 라는 Knowledge Tracing 모델을 소개하며 언급되겠지만, Knowledge Tracing을 위해서는 사용자, 학습하고자 하는 대상(여기서는 토큰 혹은 품사 등), 그리고 그 대상의 정오답 여부, 마지막으로 시간 정보가 필요합니다. 이 데이터들을 통해 사용자별로 순차적인 학습대상의 공부를 통해 정오답 여부가 어떻게 변화하였는지를 LSTM 모델을 통해 학습을 진행하게 됩니다.</p>
<h3 id="txt-to-dataframe">txt to Dataframe</h3>
<p>저는 사용자별 취약 품사 추천을 목표로 하고있습니다. 따라서, 임의의 사용자가 문장을 연습한 후, 각 품사들의 맞출 확률 혹은 정오답을 예측해야 하며 이를 위해 데이터에서 정의된 품사(Part Of Speech), 그에 0과1로 태깅된 정오답을 시간순서대로(days) 학습하고자, 해당 컬럼만을 추출하여 데이터 프레임으로 정의해주었습니다.</p>
<pre><code class="language-python">train_data = open(&quot;data/train.txt&quot;, &#39;r&#39;)
line = train_data.readline()
line_list = []

while line:
  if(line.find(&#39;# user:&#39;) == 0):
    user = line[7:line.find(&#39;  countries&#39;)]
    days = line[line.find(&#39;days&#39;)+5:line.find(&#39; &#39;,line.find(&#39;days&#39;)+4)]
    time = line[line.find(&#39;time&#39;)+5:line.find(&#39;\n&#39;)]
  elif(line.find(&#39;#&#39;) == -1 and line != &#39;\n&#39;):
    string_list = line.split()
    code = string_list[0]
    word = string_list[1]
    part_of_speech = string_list[2]
    correct = string_list[-1]
    new_data = {
        &#39;user&#39; : user,
        &#39;code&#39; : code,
        &#39;word&#39; : word,
        &#39;pos&#39; : part_of_speech,
        &#39;time&#39;: time,
        &#39;correct&#39; : correct,
        &#39;days&#39; : days,
        &#39;split&#39; : splitid
    }
    train_df = train_df.append(new_data, ignore_index=True)
  elif(line == &#39;\n&#39;):
    splitid+=1

  line = train_data.readline()

else:
  line = train_data.readline()

train_data.close()
</code></pre>
<p>위 코드는 다운받은 데이터(txt)를 데이터프레임으로 바꿔주기 위한 코드입니다. 
원본 데이터는 <code>.train</code> 파일이었는데, 이 파일을 다루는 법을 몰라, 데이터를 txt 파일로 변환하여, txt파일을 데이터프레임으로 바꿔주었습니다.</p>
<p>txt파일에서 컬럼들은 모두 탭혹은 띄어쓰기로 구분되어 있고, 그 속에서 원하는 컬럼을 뽑기 위해 find 함수를 사용하느라 시간이 매우 오래 걸렸습니다. 더 효율적인 방법으로 변환할 수 있는 방법이 분명 있을 듯 합니다.</p>
<p>아무튼 앞서 말한대로, 사용자, 품사, 정오답, 학습시간(days) 외에, 문장을 구분해주기위해 문장별 인덱스(split), 해당 문장을 연습하는 데 걸린 시간(time) 을 추가로 저장해주었습니다. </p>
<p>이렇게 하면 DKT 적용을 위한 데이터는 모두 준비되었습니다. 해당 데이터를 가지고 DKT(Depp Knowledge Tracing)에 돌려본 결과, 그리고 간단한 Knowledge Tracing에 대한 설명을 다음 게시글로 정리하겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[딥러닝 모델] CNN(Convolutional Neural Network)]]></title>
            <link>https://velog.io/@nayeon_p00/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EB%AA%A8%EB%8D%B8-CNNConvolutional-Neural-Network</link>
            <guid>https://velog.io/@nayeon_p00/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EB%AA%A8%EB%8D%B8-CNNConvolutional-Neural-Network</guid>
            <pubDate>Sat, 16 Apr 2022 09:46:25 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://halfundecided.medium.com/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-cnn-convolutional-neural-networks-%EC%89%BD%EA%B2%8C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-836869f88375">CNN 쉼게 이해하기 참고 블로그 </a></p>
</blockquote>
<h2 id="cnn-등장배경">CNN 등장배경</h2>
<p>일반 DNN은 기본적으로 1차원 형태의 데이터를 사용한다. 따라서 이미지가 입력될 경우, 이것을 flatten 시켜서 한줄의 데이터로 만들게 된다. 이 과정에서 이미지의 공간적 정보가 손실되어, 특징 추출과 학습이 비효율적이고 정확도의 한계가 발생한다는 문제가 있었다. 그래서 CNN(Convolutional Neural Network)을 통해 이미지를 raw input으로 받음으로써, 공간적/지역적 정보를 그대로 유지한채 특성들의 계층을 빌드업하게 된다. </p>
<h2 id="cnn-작동원리">CNN 작동원리</h2>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/e6e205e9-4bd7-4789-bf58-8c1b42fde7bd/image.png" alt=""></p>
<p>예를 들어 위 그림처럼, 2차원 이미지를 행렬로 표현할 수 있다. 그리고 CNN에는 필터(커널)이 존재하는데, 오른쪽에 표현된 것처럼 3x3크기의 필터가 있다고 생각한다. 이 필터를 이미지의 입력값에 전체적으로 훑어주면서 이미지의 패턴을 찾아 처리할 수 있다. </p>
<p>이때 훑어준다는 것은 연산처리를 해준다는 것이고, matrix와 matrix간의 inner product를 진행한다.</p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/a2887992-b7c9-46c2-8a0f-2390ad991176/image.png" alt=""></p>
<p>먼저 위 input image에서 순서대로 필터와 같은 크기 부분과 필터의 inner product 연산을 해주면 아래 빨간색 테두리 처럼 해당위치의 결과값은 4가 나온다. 이과정을 순서대로 위치를 옮겨가며 진행하게 되면, </p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/218f9c49-9030-4e02-9cb3-160766b82ef3/image.png" alt=""></p>
<p>위 result에 나와 있는 것처럼, 3x3 의 결과 행렬이 나오게 된다.</p>
<h2 id="cnn-전체구조">CNN 전체구조</h2>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/fd54c1fc-8d3d-44af-b47e-a38c82c54ff4/image.png" alt=""></p>
<h3 id="첫번째-convolutional-layer">첫번째 Convolutional Layer</h3>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/ca1c7f1e-81a8-485b-88ec-0148ca0f6a4d/image.png" alt=""></p>
<p>먼저 이미지를 대상으로 여러개의 필터를 사용해서 결과값을 얻는다. 위 그림에서는 28x28 이미지 입력값에 10개의 5x5필터를 사용해서 10의 24x24의 결과값들을 만들어냈다. 이 후에 활성화 함수를 적용하게 되면 첫번째 convolutional layer가 완성된다. </p>
<h3 id="첫번째-pooling-layer">첫번째 Pooling Layer</h3>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/ad0c4d70-8efc-4df1-aa07-4c0f329e7686/image.png" alt=""></p>
<p>Pooling이란 결과값의 차원을 축소해주기 위한 작업으로, 각 결과값의 크기를 줄일 수 있다. 
<img src="https://velog.velcdn.com/images/nayeon_p00/post/0f53f68b-8ac9-471b-949e-24e995cd8f14/image.png" alt=""></p>
<p>위 그림에서는 대표적으로 Max pooling 과 Average pooling을 소개하고 있으며, pool의 크기에 맞춰 matrix에서 가장 큰 값을 가져와 구성하거나, 평균을 내어 결과값의 크기를 줄여주는 작업이다.</p>
<p>따라서 pooling layer를 거치게 되면 결과값이 12x12 matrics가 된 것을 확인할 수 있다. </p>
<h3 id="두번째-covolutional-layer">두번째 Covolutional Layer</h3>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/1ceaee6f-6462-4942-b3a0-026cb18b616d/image.png" alt=""></p>
<p>다음 Convolutional layer에서는 텐서 convolution을 적용한다. 이전 pooling layer에서 얻어낸 12x12x10텐서를 대상으로, 5x5x10크기의 텐서필터20개를 사용해준다. 그 결과 각각 8x8크기를 가진 결과값 20개를 얻어낼 수 있게된다.</p>
<h3 id="두번째-pooling-layer">두번째 Pooling Layer</h3>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/1ea6e932-7801-4340-8993-1eb3c729f0f4/image.png" alt=""></p>
<p>첫번째 Pooling과 같은 방식으로 처리해주면 더 크기가 작아진 20개의 4x4결과값을 얻게된다.</p>
<h3 id="flattenvectorization">Flatten(Vectorization)</h3>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/42418987-374e-4571-88bc-87438e44a747/image.png" alt=""></p>
<p>그 후 4x4x20텐서를 일자형태의 데이터로 쭉 펼쳐주는 Flatten 작업을 거친다. 최종적으로 320 차원을 가진 벡터형태가 완성된다.</p>
<p>이렇게 데이터를 펼쳐도 가능한 이유는 두번째 Pooling layer에서 얻어낸 4x4이미지가 입력된 이밈지에서 얻어온 특이점 데이터가 되기 때문이다.</p>
<h3 id="fully-connected-layersdense-layers">Fully Connected Layers(Dense Layers)</h3>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/e50bd7d8-cdfe-4e63-a821-b4ef99ccf223/image.png" alt=""></p>
<p>마지막으로 Fully-Connected Layer를 적용하고 softmax activation function을 적용하면 최종 결과값이 도출된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[딥러닝 기초개념] 기울기 소실과 폭주 (Gradient Vanishing / Exploding)]]></title>
            <link>https://velog.io/@nayeon_p00/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EA%B8%B0%EC%B4%88%EA%B0%9C%EB%85%90-%EA%B8%B0%EC%9A%B8%EA%B8%B0-%EC%86%8C%EC%8B%A4%EA%B3%BC-%ED%8F%AD%EC%A3%BC-Gradient-Vanishing-Exploding</link>
            <guid>https://velog.io/@nayeon_p00/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EA%B8%B0%EC%B4%88%EA%B0%9C%EB%85%90-%EA%B8%B0%EC%9A%B8%EA%B8%B0-%EC%86%8C%EC%8B%A4%EA%B3%BC-%ED%8F%AD%EC%A3%BC-Gradient-Vanishing-Exploding</guid>
            <pubDate>Sat, 09 Apr 2022 05:56:25 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://wikidocs.net/61375">https://wikidocs.net/61375</a></p>
</blockquote>
<h2 id="기울기-소실-gradient-vanishing">기울기 소실 (Gradient Vanishing)</h2>
<hr>
<p>역전파 과정에서 입력층으로 갈수록, 기울기가 점차적으로 작아지는 현상이 발생할 수 있다. 이러한 현상으로 입력층에 가까운 층들에서 가중치들이 업데이트가 제대로 되지 않으면 결국 최적의 모델을 찾을 수 없게 되고, 이것을 기울기 소실이라 한다.</p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/a3599f8f-a3aa-457c-aef7-52434b696998/image.png" alt=""></p>
<p>활성화함수 중 sigmoid를 사용하게 되면, 해당 함수의 특성에 따라 기울기가 0에 수렴하여 나타나는 경우가 발생한다. 따라서 역전파에서 입력층으로 갈수록 sigmoid함수의 미분을 연쇄적으로 곱하게 될때, 그 값은 1보다 작은 값들이므로 곱할수록 값이 점점 작아지게 된다. layer가 많으면 많을수록 기울기 값은 0에 가깝게 작아져서 가중치의 변화가 거의 없게 되고, error값도 더이상 줄어들지 않게된다. </p>
<br/>

<h2 id="기울기-폭주-gradient-exploding">기울기 폭주 (Gradient Exploding)</h2>
<hr>
<p>기울기 소실과 반대로, 기울기가 입력층으로 갈수록 점차 커지다가 가중치들이 비정상적으로 큰 값이 되면서 발산되는 현상이다.</p>
<br/>

<h2 id="기울기-소실과-폭주-방지">기울기 소실과 폭주 방지</h2>
<h3 id="활성화함수-relu--leaky-relu-사용">활성화함수 ReLU / Leaky ReLU 사용</h3>
<hr>
<p>기울기 소실에서 sigmoid 활성화함수를 사용하여 기울기가 0에 수렴하는 문제가 있었다. 그래서 역전파과정에서 전파시킬 기울기가 점점 사라진다는 문제가 있었는데, 이를 해결하기 위해 hidden layer의 활성화함수로 sigmoid나 tanh대신 ReLU나 ReLU의 변형함수인 Leaky ReLU를 사용하는 것이다. </p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/9ce180e1-7f4d-408b-9bd2-d265d61e024f/image.png" alt=""></p>
<p>특히 Leaky ReLU를 사용하면 모든 입력값에 대해 기울기가 0에 수렴하지 않으므로 죽은 ReLU문제를 해결할 수 있다. </p>
<br/>

<h3 id="그래디언트-클리핑-gradient-clipping">그래디언트 클리핑 (Gradient Clipping)</h3>
<hr>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/3dd885e4-dd8d-4683-887c-8e55fe987beb/image.png" alt=""></p>
<p>그래디언트 클리핑이란 기울기 값을 자르는 것을 의미한다. 기울기 폭주를 막기 위해서 특정 threshold를 넘지 않도록 값을 잘라 threshold만큼 크기를 감소시키는 것이다. 클리핑은 기울기의 L2norm으로 나눠주는 방식으로 하게되는데, threshold는 gradient가 가질 수 있는 최대 L2norm을 뜻한다.</p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/1002c629-ab8f-40aa-95c9-a0b0637b11c9/image.png" alt=""></p>
<p>위 그림처럼 기울기가 많이 뛰어서 global minimum에 도달하지 못하고 엉뚱한 방향으로 가는 것을 클리핑이 막아준다. 이때 gradient vector의 방향은 유지하면서 적은 값만큼 안정적으로 이동할 수 있다.</p>
<br/>

<h3 id="가중치-초기화-weight-initialization">가중치 초기화 (Weight initialization)</h3>
<hr>
<p>같은 모델을 훈련하더라도 가중치가 초기에 어떤 값을 가졌느냐에 따라 모델의 훈련결과가 달라지기도 한다. 초기값을 0으로 하면 역전파 계산시 모든 가중치의 값이 똑같이 갱신되므로 적절한 가중치로 초기화하는 것이 중요하다.</p>
<ul>
<li><h4 id="세이비어-초기화-xavier-initialization">세이비어 초기화 (Xavier Initialization)</h4>
</li>
</ul>
<p>이전 Layer의 노드 수와 다음 Layer의 노드 수에 따라 가중치를 결정 짓는 방법이다. Uniform 분포를 따르는 방법으로 초기화하는 방법과, Normal 분포를 따르는 방법 두가지가 사용된다.</p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/f765f9cc-dc39-4128-af20-ee3f97d3d082/image.png" alt=""></p>
<p>세이비어 초기화는 여러층의 기울기 분산 사이에 균형을 맞춰 특정층이 너무 주목을 받거나 다른 층이 뒤쳐지는 것을 막는다.
S자 형태인 활성화 함수와 함께 사용할 때는 좋은 성능을 보이지만, ReLU와 함께 사용하면 성능이 좋지 않다.</p>
<ul>
<li><h4 id="he-초기화-he-initialization">He 초기화 (He Initialization)</h4>
</li>
</ul>
<p>세이비어 초기화에서는 ReLU함수를 사용할 때 비효율적이라는 것을 보이는데, 이것을 보완한 기법이다. 이때는 다음층의 뉴런수를 반영하지 않으며 여기서도 Uniform 분포를 따르는 방법으로 초기화하는 방법과, Normal 분포를 따르는 방법 두가지가 사용된다.</p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/d48a4f1f-bffb-4b34-911c-da8b1ec7c407/image.png" alt=""></p>
<p>He 초기화 + ReLU가 보편적이라고 한다.</p>
<br/>

<h3 id="배치-정규화-batch-normalization">배치 정규화 (Batch Normalization)</h3>
<hr>
<p>배치정규화는 인공신경망의 각 층에 들어가는 입력을 평균과 분산으로 정규화하여 학습을 효율적으로 만든다. 각 층에서 활성화 함수를 통과하기 전에 수행된다.</p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/643c7311-b0a4-4073-904e-0611f6b85a0e/image.png" alt=""></p>
<p>학습시 배치단위의 평균과 분산들을 차례대로 받아 이동평균과 이동분산을 저장해놓았다가, 테스트할때는 해당배치의 평균과 분산을 구하지 않고 구해두었던 평균과 분산으로 정규화를 한다.</p>
<p>정규화를 통해 입력분포를 일정하게 만들기 때문에 가중치를 일관되게 계산할 수 있다. 미니배치마다 평균과 표준편차를 계산하여 사용하므로, 오버피팅을 방지할 수도 있으며, 드롭아웃과 비슷한 효과를 낼 수 있다. </p>
<p>그러나 배치정규화는 미니배치 크기에 의존적이며, RNN에 적응하기 어렵다는 단점이 있다. (각 시점마다 다른 통계치를 가지기 때문이라고 함.)</p>
<br/>

<h3 id="층-정규화-layer-normalization">층 정규화 (Layer Normalization)</h3>
<hr>
<p>따라서 층 정규화 라는 방법이 소개된다.</p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/2d9c35c4-2cbc-4825-9ab0-d03c01df7752/image.png" alt=""></p>
<p>위 그림은 배치정규화를 시각화한 그림</p>
<p><img src="https://velog.velcdn.com/images/nayeon_p00/post/f604bcff-bebb-4477-bf8d-89005bf5d5a6/image.png" alt=""></p>
<p>층 정규화는 데이터별로 정규화하는 것이다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[딥러닝 기초개념] Dropout]]></title>
            <link>https://velog.io/@nayeon_p00/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EA%B8%B0%EC%B4%88%EA%B0%9C%EB%85%90-Dropout-kxz53fo0</link>
            <guid>https://velog.io/@nayeon_p00/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EA%B8%B0%EC%B4%88%EA%B0%9C%EB%85%90-Dropout-kxz53fo0</guid>
            <pubDate>Sun, 03 Apr 2022 14:46:21 GMT</pubDate>
            <description><![CDATA[<h2 id="dropout">Dropout</h2>
<p>네트워크의 유닛의 일부만 동작하도록하고, 일부는 동작하지 않도록 하는 방법이다.</p>
<p><img src="https://media.vlpt.us/images/nayeon_p00/post/bc7ed40e-027f-431d-85e0-478cd74e6fe5/image.png" alt=""></p>
<p>dropout은 <strong>1. 오버피팅을 방지</strong>하기 위한 방법 중 하나이며, hidden layer의 일부 유닛을 동작하지 않게 하는 것이다.</p>
<p><img src="https://media.vlpt.us/images/nayeon_p00/post/1d446d32-0784-4084-9aa7-633f406d7619/image.png" alt=""></p>
<p>hidden layer에 드롭아웃을 확률 p로 적용할 때, hidden 유닛들은 p확률로 제거되는 것이다. </p>
<p>z는 0혹은 1을 가질수 있는 베르누이 분포 랜덤변수 벡터로, 확률적으로 h와 곱해지면서 hidden layer안에 있는 몇개의 노드를 날려버릴 수 있다. 이때 확률은 일반적으로 0.5이지만 하이퍼파라미터로 지정할 수 있다. </p>
<pre><code class="language-py">dropout = torch.nn.Dropout(p=drop_prob)

model = torch.nn.Sequential(linear1, relu, dropout,
                    linear2, relu, dropout,
                    linear3, relu, dropout).to(device)</code></pre>
<p>위 코드처럼 여러 레이어를 쌓으며 그 사이에 dropout을 취해주면서 여러형태의 네트워크를 구성할 수 있고 <strong>2. 앙상블 효과</strong>도 낼 수 있게 된다.</p>
<p><img src="https://media.vlpt.us/images/nayeon_p00/post/8a82c68b-1513-4461-ab83-b9f5987963aa/image.png" alt=""></p>
<p>또 위 그림처럼 뉴런이 어떤 하나의 입력에만 의존할 수 없어지기 때문에, 입력노드들의 <strong>3. 가중치를 분산</strong>하는 효과를 낼 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[딥러닝 기초개념] 최적화 (Optimization)]]></title>
            <link>https://velog.io/@nayeon_p00/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EA%B8%B0%EC%B4%88%EA%B0%9C%EB%85%90-%EC%B5%9C%EC%A0%81%ED%99%94-Optimization</link>
            <guid>https://velog.io/@nayeon_p00/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EA%B8%B0%EC%B4%88%EA%B0%9C%EB%85%90-%EC%B5%9C%EC%A0%81%ED%99%94-Optimization</guid>
            <pubDate>Sun, 03 Apr 2022 12:52:27 GMT</pubDate>
            <description><![CDATA[<h1 id="최적화-optimization">최적화 (Optimization)</h1>
<p>loss 함수의 최소값을 찾아가는 것</p>
<br/>

<h1 id="최적화-알고리즘-optimizer">최적화 알고리즘 (Optimizer)</h1>
<h2 id="gradient-descent-algorithm">Gradient Descent Algorithm</h2>
<hr>
<p>경사하강법이란, 네트워크의 파라미터들을 θ(W,b)라 했을 때, Loss function J(θ)의 optima(최소화)를 찾기위해 파라미터의 기울기(gradient)를 이용하는 방법이다.</p>
<p><img src="https://media.vlpt.us/images/nayeon_p00/post/de13e278-84a5-43a8-8fb4-119e934e0b01/image.png" alt=""></p>
<p>알파는 learning rate에 해당하며, 알파에 loss funtion을 미분한 값을 곱해 빼주며 갱신한다. learning rate는 갱신되는 속도를 결정한다.
<img src="https://media.vlpt.us/images/nayeon_p00/post/d1521985-a668-49bd-b672-cdc9cac20e10/image.png" alt=""></p>
<p>그래서 위 그림처럼 초기값부터 시작하여 최종적인 minimum에 도달하는 것이 최종목표가 되며 아래는 learning rate에 따른 수렴정도를 나타낸 그림이다.</p>
<p><img src="https://media.vlpt.us/images/nayeon_p00/post/a6df2aee-83d2-4f5c-ab48-644e7e3c0523/image.png" alt=""></p>
<p>Gradient Descent Algorithm은 데이터 크기에 따라 세가지로 나뉜다.</p>
<ul>
<li>Batch Gradient Descent (BGD) : 전체 데이터 셋에 대한 에러를 구한 뒤 기울기를 한번만 계산 후 파라미터 업데이트
: loss가 안정적으로 수렴하지만 학습이 오래걸리며 local minima에 걸릴 수 있다.</li>
<li>Stochastic Gradient Descent (SGD) : 임의의 하나의 데이터에 대해 에러를 구한 뒤 기울기를 계산 후 파라미터 업데이트
: local minima에 빠질 위험이 적지만 optimal을 찾지 못할 가능성이 있다.</li>
<li>Mini-batch Gradient Descent (MGD) : 전체 데이터셋에서 뽑은 mini-batch의 데이터에 대해서 각 데이터에 대한 기울기를 계산 후 그것의 평균기울기를 통해 파라미터 업데이트
<img src="https://media.vlpt.us/images/nayeon_p00/post/08756016-9a58-49b3-8ade-0d4820c97951/image.png" alt="">
위 그림처럼 mini-batch 별 기울기를 구해 평균 기울기를 계산하고, 그때마다 파라미터들을 업데이트하는 방식이다.</li>
</ul>
<br/>

<h2 id="momentum-algorithm">Momentum Algorithm</h2>
<hr>
<p>Gradient Descent의 단점은 기울기가 0인 점을 잘 벗어나지 못한다는 점과, 학습이 느리다는 점이 있었다. 이를 보완하기 위한 방법이 관성, momentum을 적용하는 것이다. 관성은 변수가 가던 방향으로 계속 가도록 하는 속도항을 추가하는 것인데, 기울기가 0이더라도 속도가 있어 더 잘 탈출하게 된다.</p>
<p><img src="https://media.vlpt.us/images/nayeon_p00/post/2d08492b-ca19-40bf-81ab-3d0b41573b65/image.png" alt=""></p>
<p>Vt는 이전 이동거리와 관성계수 m에 따라 파라미터를 업데이트하도록 수식이 적용된다.</p>
<p><img src="https://media.vlpt.us/images/nayeon_p00/post/1fcebf0f-0674-43bd-ae52-0108a631a452/image.png" alt=""></p>
<p>위 그림에서 momentum(관성)이 없다면 최소값을 향해 왔다갔다하며 접근할 때, learning rate에 맞춰 진동폭이 동일하게 접근하게 되고, 만약 learning rate가 훨씬 크다면 발산할 가능성도 있다.
그러나 관성을 이용하면, 세로축에 대해서는 느리게, 가로축방향으로는 빠르게 움직여 더 최소값에 잘 접근하도록 적용할 수 있게 된다. 이를 Gradient descent with momentum으로 구현가능하다.</p>
<h3 id="gradient-descent-with-momentum">Gradient descent with momentum</h3>
<p>weight의 derivative(dW)에 대한 가중 평균치 velocity(v, 속도항)를 구해 문제를 해결하는데,</p>
<p><img src="https://media.vlpt.us/images/nayeon_p00/post/deb972f0-cedc-4d05-ad16-b17958fdcee8/image.png" alt=""></p>
<p>현재 미니배치에서 dW와 dB를 구하고, dW와 dB에 대해 지수가중평균을 구한다. 그 지수가중평균을 가지고 W, b를 업데이트한다. 나아가야할 방향의 변동 평균은 꽤 큰값이 되어 더 높은 속도로 최소값에 접근할 수 있게 된다. dW,db는 가속도 역할, V는 속도의 역할이다.</p>
<blockquote>
<h4 id="지수가중평균">지수가중평균</h4>
<p><img src="https://media.vlpt.us/images/nayeon_p00/post/6aa80da8-e93a-403c-acb7-4bde762f3838/image.png" alt="">
오래된 데이터 일수록 현재의 경향에 미치는 영향이 줄어든다는 의미이다.</p>
</blockquote>
<br/>

<h2 id="adaptive-gradientadagrad">Adaptive Gradient(Adagrad)</h2>
<hr>
<p>지금까지 많이 변화한 매개변수는 적게 변화하도록하고, 적게 변화한 매개변수는 많이 변화하도록 learning rate의 값을 조절하는 개념이다.</p>
<p><img src="https://media.vlpt.us/images/nayeon_p00/post/24dfaba0-da8a-4459-a683-209926566680/image.png" alt=""></p>
<p>기존 GD에서 h를 곱해 학습률에 개입하게 된다. h에는 매번 갱신될 때마다 해당 매개변수의 기울기값을 제곱하여 넣게되고, 처음에는 학습률을 높이고 많이 이동할수록 학습률을 낮출 수 있게된다. 따라서 learning rate를 반비례로 적용하게 된다. </p>
<p>그러나 학습이 진행될수록 변화폭이 너무 줄게 되면 움직이지 않게된다는 단점이 있다.</p>
<br/>


<h2 id="rmsprop-algorithm">RMSprop Algorithm</h2>
<hr>
<p>Root Mean Square Propatation의 약자로, 기울기 강하의 속도를 증가시키는 알고리즘이다. Adagrad는 과거의 기울기를 제곱해서 더하는 방식이므로 점점 갱신정도가 약해지는데, 변화폭이 너무 줄게 되면 갱신량이 0이 되어 갱신되지 않는 문제점이 있었다. 이러한 문제를 해결하기 위해 과거의 기울기를 똑같이 더하는 것이 아니라, 먼 과거의 기울기는 조금 반영하고 최신의 기울기는 많이 반영하도록 지수이동평균을 적용했다. </p>
<p><img src="https://media.vlpt.us/images/nayeon_p00/post/9a98010d-8f5e-481c-b912-0f2974df8bb3/image.png" alt=""></p>
<p>학습이 진행됨에 따라 학습속도가 지속적으로 줄어들어 0에 수렴하는 것은 방지할 수 있다. </p>
<br/>

<h2 id="adam-optimization-algorithm">Adam Optimization Algorithm</h2>
<hr>
<p>Adaptive Moment Estimation의 약자로, momentum과 PMSprop을 섞어놓은 최적화 알고리즘이기 때문에 딥러닝에서 가장 많이 사용되는 옵티마이저이다.</p>
<p><img src="https://media.vlpt.us/images/nayeon_p00/post/b6b4a1db-127b-45b4-a440-71290a5a5022/image.png" alt=""></p>
<p>먼저 초기화를 진행하고, momentum 과 RMSprop에서 사용한 v, S를 지정해준다.</p>
<p><img src="https://media.vlpt.us/images/nayeon_p00/post/ca73f620-f20a-4502-9398-ed6244e4d729/image.png" alt=""></p>
<p>바이어스 보정을 진행한다.</p>
<blockquote>
<h4 id="바이어스-보정bias-correction">바이어스 보정(bias correction)</h4>
<p>지수가중평균을 이용한 추정은 초기구간에 오차가 있기 때문에, 바이어스 보정을 통해 보완할 수 있다.
<img src="https://media.vlpt.us/images/nayeon_p00/post/0d8cbb4e-af50-40c9-84b1-b82a8b0b3cff/image.png" alt=""></p>
</blockquote>
<p><img src="https://media.vlpt.us/images/nayeon_p00/post/45b87fb1-b79b-4cc5-af1d-4f75ac2f1781/image.png" alt=""></p>
<p>momentum과 RMSprop의 가중치 업데이트 방식을 모두 사용해 가중치 업데이트를 진행한다.</p>
<p><img src="https://media.vlpt.us/images/nayeon_p00/post/26cb254f-fbb5-49ed-b77d-396c28204032/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[딥러닝 기초개념] 손실함수]]></title>
            <link>https://velog.io/@nayeon_p00/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EA%B8%B0%EC%B4%88%EA%B0%9C%EB%85%90-%EC%86%90%EC%8B%A4%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@nayeon_p00/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EA%B8%B0%EC%B4%88%EA%B0%9C%EB%85%90-%EC%86%90%EC%8B%A4%ED%95%A8%EC%88%98</guid>
            <pubDate>Sun, 27 Mar 2022 09:48:01 GMT</pubDate>
            <description><![CDATA[<h1 id="손실함수">손실함수</h1>
<p>타켓의 실제값과 도출한 예측값의 차이를 수치화해주는 함수이다. 오차가 클수록 손실함수의 값이 크고, 오차가 작을수록 손실함수의 값이 작아진다. 그래서 모델성능의 &#39;나쁨&#39;의 정도를 나타낼 수 있다.
손실함수의 값(loss)을 최소화하는 W(가중치), b를 찾아가는 것이 학습 목표이다.</p>
<h1 id="손실함수의-종류">손실함수의 종류</h1>
<h2 id="평균-제곱오차mean-squared-error-mse">평균 제곱오차(Mean Squared Error, MSE)</h2>
<p><img src="https://images.velog.io/images/nayeon_p00/post/d9fac276-d75b-4a10-8090-a9b8be49c0f0/image.png" alt=""></p>
<p>실제 정답에 대한 정답률의 오차뿐 아니라, 다른 오답에 대한 정답률의 오차도 포함하여 계산해준다. 최적값에 가까워질수록 이동값이 다르게 변화하기 때문에 최적값에 수렴하기 용이하다.</p>
<p>값을 제곱하기 때문에 절댓값이 1미만인 값은 더 작아지고, 1보다 큰 값은 더 커지는 왜곡이 발생할 수 있다.</p>
<h2 id="평균-절대값오차mean-absolute-error-mae">평균 절대값오차(Mean Absolute Error, MAE)</h2>
<p><img src="https://images.velog.io/images/nayeon_p00/post/ef094c6e-c0d4-4f2f-8454-f0a246eeaa74/image.png" alt=""></p>
<p>어떤식으로 오차가 발생했는지, 음수인지 양수인지 알 수없다.
최적값에 가까워져도 이동거리가 항상 일정하기 때문에 최적값에 수렴하기가 어렵다.</p>
<p>그렇지만 MAE는 MSE에 비해 outlier의 영향을 적게 받는다. MSE는 이상치가 멀리떨어져 있을 수록 제곱하여 그 값을 크게 만들지만, MAE는 절대값을 취하기 때문이다.</p>
<h2 id="크로스-엔트로피cross-entropy">크로스 엔트로피(Cross-Entropy)</h2>
<p>크로스 엔트로피는 카테고리컬 데이터를 분류할 때 주로 사용된다. </p>
<h3 id="엔트로피란">엔트로피란?</h3>
<p><img src="https://images.velog.io/images/nayeon_p00/post/69e0ab45-52a8-4dc3-8414-05577d724280/image.png" alt=""></p>
<p>사건 A를 반복 실행했을 때, 얻을 수 있는 평균정보량
발생활 확률이 클수록, A사건에 대해 발생할 정보량이 작아진다. 반대로, 엔트로피가 크다는 것은 예측하기가 어려운 사건일수록 정보량이 많아지고, 엔트로피가 커지게 된다.</p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/5e50cfc1-11ac-4d2c-9ef6-1322d33c0c57/image.png" alt=""></p>
<h3 id="크로스-엔트로피-오차cross-entropy-error-cee">크로스 엔트로피 오차(Cross Entropy Error, CEE)</h3>
<p><img src="https://images.velog.io/images/nayeon_p00/post/a01faaf0-c8bf-4093-9818-e706d8274cc7/image.png" alt=""></p>
<p>엔트로피 공식을 기반으로, 각 사건이 발생할 확률이 몇가지 인지에 따라 공식은 조금씩 바뀐다.
Q(x)는 신경망의 출력값, P(x)는 정답 레이블인데, 정답레이블은 정답만 1이고, 나머지는 0인 onehot 벡터를 사용한다. 
P(x)는 원핫벡터이기 때문에, 정답1이 있는 위치만 1 * InQ(m) 으로 나오게 되고 나머지는 0으로 도출되어 정답위치에 해당하는 값이 CEE로 출력된다.</p>
<p>정보량이 0에 가까워져 발생확률이 1에 가깝게 만드는 것을 목적으로 한다.</p>
<h3 id="이진-크로스-엔트로피-오차binary-cross-entropy-error-bcee">이진 크로스 엔트로피 오차(Binary Cross Entropy Error, BCEE)</h3>
<p><img src="https://images.velog.io/images/nayeon_p00/post/029cb03c-b7e9-4b0c-98a3-452e7ddf3454/image.png" alt=""></p>
<p>교차엔트로피는 분류할 클래스가 2보다 클 때 사용할 수 있지만, 이진 크로스엔트로피 오차는 클래스가 0과 1일때만 고려하여 계산하는 방식이다.
y_hat은 예측값이고, y는 실제값이다. </p>
<h4 id="유도과정">유도과정</h4>
<p><img src="https://images.velog.io/images/nayeon_p00/post/7faa2968-3162-4864-a3e6-8f11aad36a75/image.png" alt=""></p>
<p>y=0의 CEE 공식</p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/e3e06d17-463e-4ce3-98a3-0059347c418e/image.png" alt=""></p>
<p>y=1의 CEE 공식</p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/a2328116-8486-4067-bd9c-a015e358b1bf/image.png" alt=""></p>
<p>합침</p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/70c0e986-10a9-4414-8477-a700ab030629/image.png" alt=""></p>
<p>N개의 학습데이터 전체에 대한 교차엔트로피를 구하는 공식(평균)이다.
BCEE는 출력층의 노드 수를 하나로 하여 출력값을 하나로 받기때문에, 실제값과 예측값 모두 하나의 스칼라 값이다.</p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/e9dab4ed-d40f-4171-9c7b-ce4fa45e2a0f/KakaoTalk_20220327_190344299.jpg" alt=""></p>
<h3 id="범주형-크로스-엔트로피-오차categorical-cross-entropy-error-ccee">범주형 크로스 엔트로피 오차(Categorical Cross Entropy Error, CCEE)</h3>
<p>클래스가 3개 이상인 데이터를 대상으로 사용하는 손실함수이고, 타겟 라벨은 원핫벡터로 구성, 출력된 벡터는 각 클래스에 속할 총합1인 확률로 나온다.
CEE를 N개의 데이터셋에 대해 1개의 스칼라를 추출하는 방법이다.</p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/93c99e36-a0ac-4fe5-9784-d15c1ca89bb4/image.png" alt=""></p>
<p>데이터셋 개수 N, 클래스 개수 C
각각의 데이터셋에서 도출된 CEE 구하고, 평균을 구하는 방식</p>
<blockquote>
<p><img src="https://images.velog.io/images/nayeon_p00/post/ad81d8af-6afa-4344-963b-95862f6e08b0/image.png" alt=""> 5개의 클래스에 대해 원핫벡터로 표현된 label들, 각 데이터셋에서 도출된 예측값 predict, 각 데이터 셋에서의 CEE를 구하고, 이를 합쳐 평균으로 나타낸 CCEE
<a href="https://gooopy.tistory.com/65?category=824281">https://gooopy.tistory.com/65?category=824281</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[딥러닝 기초개념] 활성화 함수]]></title>
            <link>https://velog.io/@nayeon_p00/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EA%B8%B0%EC%B4%88%EA%B0%9C%EB%85%90-%ED%99%9C%EC%84%B1%ED%99%94-%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@nayeon_p00/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EA%B8%B0%EC%B4%88%EA%B0%9C%EB%85%90-%ED%99%9C%EC%84%B1%ED%99%94-%ED%95%A8%EC%88%98</guid>
            <pubDate>Sat, 19 Mar 2022 10:36:52 GMT</pubDate>
            <description><![CDATA[<h1 id="활성화함수의-필요성">활성화함수의 필요성</h1>
<p><img src="https://images.velog.io/images/nayeon_p00/post/31cff61d-5619-4a85-96fc-d9b06f992128/KakaoTalk_20220319_162436665.jpg" alt=""></p>
<p>우선 퍼셉트론에서, x1와 x2를 입력받아 y를 출력하게 된다. 이때 b라는 편향을 통해 퍼셉트론이 얼마나 쉽게 활성화 되는지에 대한 정도를 결정한다. 편향이 -1000이라면, 입력값이 1000이 넘어야 활성화 될 것이고, 편향이 -10이라면, 입력값이 10만 넘어도 쉽게 활성화 될 수 있다. 노이즈를 제거하기 위한 목적도 있는데, 노이즈만큼을 편향으로 주어 노이즈로 발생할 값들을 무시해주는 역할도 할 수 있다.</p>
<p>위 그림에서의 퍼셉트론은 x1, x2, 1이라는 세개의 신호가 뉴런에 입력되고 각 신호에 가중치를 곱한 후 다음 뉴런에 전달된다. 이 신호의 총합이 h()라는 함수를 거쳐 변환되고, 그 변환값이 y의 출력이 되는 것이다. 이때 h()처럼 입력 신호의 총합을 출력신호로 변환하는 함수를 활성화 함수라고 한다.</p>
<p>활성화함수는 훈련과정에서 계산량이 많고, 역전파(backpropagation)에서 사용된다. 각 활성화함수에서 계산된 미분값을 통해 backpropagation이 이루어지고, 이를 통해 손실을 줄이도록 최적화해 나간다.</p>
<h2 id="비선형-활성함수의-필요성">비선형 활성함수의 필요성</h2>
<p>이 활성화함수를 통해 비선형함수를 사용하여 레이어층을 깊게 표현할 수 있다. 선형함수를 사용하게 되면 아무리 층을 쌓아도, 결국 선형함수로만 표현되어 층을 쌓지 않아도 같은 선형함수로 표현가능하게 된다. 따라서 비선형함수를 사용하여, 각각의 층에서 적절한 학습이 이뤄지도록 한다.</p>
<h1 id="활성화함수의-종류">활성화함수의 종류</h1>
<h2 id="선형-활성화함수">선형 활성화함수</h2>
<p><img src="https://images.velog.io/images/nayeon_p00/post/2f65a19e-dff4-4647-ab5c-b56ecca0c08d/image.png" alt=""></p>
<p>선형함수를 사용하면 다중출력이 가능해서 이진분류, 다중분류문제까지 해결가능하다. </p>
<p>그러나 비선형적 특성을 지닌 데이터를 예측하지 못한다. 특히 XOR gate 같은 그래프처럼 하나의 선으로 구분할 수 없는 경우를 구현하지 못한다. </p>
<h2 id="비선형-활성화함수">비선형 활성화함수</h2>
<p><img src="https://images.velog.io/images/nayeon_p00/post/2899a1b8-0417-4655-be7d-67105534529a/image.png" alt=""></p>
<h3 id="sigmoid">Sigmoid</h3>
<p><img src="https://images.velog.io/images/nayeon_p00/post/0540b1b2-586c-4360-a318-dc2d4d6df22e/image.png" alt=""></p>
<h4 id="수식">수식</h4>
<p><img src="https://images.velog.io/images/nayeon_p00/post/abdcd054-7893-4edf-8e1d-857014384356/image.png" alt=""></p>
<h4 id="출력">출력</h4>
<p>0 에서 1사이 값</p>
<h4 id="평균">평균</h4>
<p>0.5</p>
<h4 id="특징">특징</h4>
<p>음수값을 0에 가깝게 표현하기 때문에, 입력값이 최종레이어에서 미치는 영향이 적어지는 Vanishing Gradient Problem 발생.</p>
<blockquote>
<h4 id="vanishing-gradient-problem">Vanishing Gradient Problem</h4>
<p>기울기값이 사라지는 문제. 기울기가 사라진다면, 변화량이 없어진다는 것이고, 학습할 수 없는 상태가 된다는 것. error rate가 낮아지지 못하고 수렴해버리는 문제.</p>
</blockquote>
<p>binary classification의 경우 출력층 노드가 1개이고, 이 노드 값은 0에서 1의 값을 가져야 하기 때문에, 시그모이드를 통해 0혹은 1의 값을 출력값으로 받을 수 있다.</p>
<p>은닉층에서는 시그모이드가 잘 사용되지 않는다.</p>
<hr>
<h3 id="tanh-hyerbolic-tangent">Tanh (Hyerbolic Tangent)</h3>
<p><img src="https://images.velog.io/images/nayeon_p00/post/84790ad0-e7ea-4c29-826f-b56bd8beded8/image.png" alt=""></p>
<h4 id="수식-1">수식</h4>
<p><img src="https://images.velog.io/images/nayeon_p00/post/330e3d31-3ced-4a58-90ef-49e74453b626/image.png" alt=""></p>
<h4 id="출력-1">출력</h4>
<p>-1 에서 1사이 값</p>
<h4 id="평균-1">평균</h4>
<p>0</p>
<h4 id="특징-1">특징</h4>
<p>평균이 0이므로 음과 양이 같은 편향을 가지도록 분포가 되어 있다. 0중심에서 기울기가 크기 때문에 값을 잘 보존한다.</p>
<p>이때도 시그모이드와 동일하게 Vanishing Gradient Problem이 나타난다.</p>
<hr>
<h3 id="softmax-function">Softmax Function</h3>
<h4 id="수식-2">수식</h4>
<p><img src="https://images.velog.io/images/nayeon_p00/post/8a02b1e6-7d83-40d2-9fc3-41cd9cd758c4/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/77b9c974-8ba1-4541-8e82-20563982f08a/image.png" alt=""></p>
<blockquote>
<p>시그모이드 함수로부터 유도된 softmax 식
<img src="https://images.velog.io/images/nayeon_p00/post/f839ac04-7c22-49d2-8c85-d88339ce13dc/KakaoTalk_20220320_161337529.jpg" alt=""></p>
</blockquote>
<h4 id="특징-2">특징</h4>
<p>소프트맥스는 세개 이상으로 분류하는 다중 클래스 분류에서 사용되는 활성화 함수이다. 분류될 클래스가 n개일 때, n차원의 벡터를 입력받아, 각 클래스에 속할 확률을 추정하게 된다.</p>
<hr>
<h3 id="relu-rectified-linear-unit">ReLu (Rectified Linear Unit)</h3>
<p><img src="https://images.velog.io/images/nayeon_p00/post/3712f349-0b33-42f1-a381-dcaa3b53523d/image.png" alt=""></p>
<h4 id="수식-3">수식</h4>
<p><img src="https://images.velog.io/images/nayeon_p00/post/a82fe5bc-69a2-45de-ad6f-db948e738c00/image.png" alt=""></p>
<h4 id="출력-2">출력</h4>
<p>0 에서 z</p>
<h4 id="특징-3">특징</h4>
<p>가장 많이 사용되는 활성화함수이다.</p>
<p>대부분의 input값에 대한 기울기가 0이 아니므로, 학습이 빠르다. Sigmoid, Tanh와 달리, 기울기가 0이 되는 문제를 막아주기 때문에 학습이 빠르다.</p>
<p>input이 0보다 작은 경우 기울기가 0이 되는데, 실제로 hidden layer에서 대부분 노드의 z값은 0보다 크기 때문에 기울기가 0이 되는 경우가 많지 않다.</p>
<hr>
<h3 id="leaky-relu">leaky ReLu</h3>
<p><img src="https://images.velog.io/images/nayeon_p00/post/3e4d526d-aeba-4c8d-b702-d9c46dd0f50b/image.png" alt=""></p>
<h4 id="수식-4">수식</h4>
<p><img src="https://images.velog.io/images/nayeon_p00/post/2336e933-d7b8-42e0-b472-8fbfe87ddcda/image.png" alt=""></p>
<h4 id="출력-3">출력</h4>
<p>0.01z 에서 z</p>
<h4 id="특징-4">특징</h4>
<p>input값이 음수인 경우, 기울기가 0이 아닌 0.01 값을 가지게 되므로 ReLu보다 학습이 더 잘된다. </p>
<hr>
<h3 id="elu-exponential-linear-unit">ELU (Exponential Linear Unit)</h3>
<p><img src="https://images.velog.io/images/nayeon_p00/post/3fd21951-7b7b-4ef3-88e4-a5a243cf327e/image.png" alt=""></p>
<h4 id="수식-5">수식</h4>
<p><img src="https://images.velog.io/images/nayeon_p00/post/885ff1bb-c31b-433c-bef5-fee0406ff1a0/image.png" alt=""></p>
<h4 id="출력-4">출력</h4>
<p>-1에서 z</p>
<h4 id="특징-5">특징</h4>
<p>leaky ReLu와 마찬가지로 입력값이 음수일 때 기울기가 0이 아닌 0에서 1사이 값으로 나타난다. leaky ReLu보다 큰 기울기로 수렴이 빠르다.
그러나 exponential 함수에 대한 연산비용이 필요하다.</p>
<blockquote>
<p>활성화함수 구현과 특징 한계 실험 <a href="https://github.com/ai-rtistic/AIFFEL-Project/blob/master/Fundamental/FD23_Activation_Function.ipynb">https://github.com/ai-rtistic/AIFFEL-Project/blob/master/Fundamental/FD23_Activation_Function.ipynb</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[종이 시험지 자동 채점 프로그램 | Tensorflow Object Detection API | Faster R-CNN | Ch4. 항목 감지 모델 학습하고 추론하기]]></title>
            <link>https://velog.io/@nayeon_p00/%EC%A2%85%EC%9D%B4-%EC%8B%9C%ED%97%98%EC%A7%80-%EC%9E%90%EB%8F%99-%EC%B1%84%EC%A0%90-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-Tensorflow-Object-Detection-API-Faster-R-CNN-Ch4.-%ED%95%AD%EB%AA%A9-%EB%B6%84%EB%A5%98-%EB%AA%A8%EB%8D%B8-%ED%95%99%EC%8A%B5%ED%95%98%EA%B3%A0-%EC%B6%94%EB%A1%A0%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@nayeon_p00/%EC%A2%85%EC%9D%B4-%EC%8B%9C%ED%97%98%EC%A7%80-%EC%9E%90%EB%8F%99-%EC%B1%84%EC%A0%90-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-Tensorflow-Object-Detection-API-Faster-R-CNN-Ch4.-%ED%95%AD%EB%AA%A9-%EB%B6%84%EB%A5%98-%EB%AA%A8%EB%8D%B8-%ED%95%99%EC%8A%B5%ED%95%98%EA%B3%A0-%EC%B6%94%EB%A1%A0%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 02 Jan 2022 08:01:10 GMT</pubDate>
            <description><![CDATA[<h2 id="항목-감지-모델-학습">항목 감지 모델 학습</h2>
<p>이제 앞에서 문제감지모델을 이용해 문제별로 잘라낸 이미지를 사용할 시간입니다. 사용자가 선택한 답의 번호를 확인하기 위해 문제 속 선택한 항목과 선택하지 않은 항목을 구분하는 것이 필요합니다. 따라서 먼저 앞에서 저장된 이미지들을 라벨링하였습니다.</p>
<h3 id="항목-라벨링labeling">항목 라벨링(Labeling)</h3>
<p><img src="https://images.velog.io/images/nayeon_p00/post/ca8c725e-459a-4360-ab28-f6e476730a02/image.png" alt=""></p>
<p>위 사진처럼 번호를 각각 box로 라벨링을 진행하였고, 각각 not-choice, choice 두개의 클래스로 나뉘어 집니다.</p>
<h3 id="모델-학습">모델 학습</h3>
<p>여기서도 faster rcnn 모델을 사용했습니다. 그 중 inception resnet atrous v2 신경망을 사용했으며, 생성한 데이터셋으로 수정한 config 파일을 gitHub에 업로드 하였습니다. config파일 속 수정되어야 하는 부분은 아래와 같습니다.</p>
<p><code>10 num_classes: 1</code></p>
<p><code>108 fine_tune_checkpoint: &quot;faster_rcnn_inception_resnet_v2_atrous_coco_2017_11_08/model.ckpt&quot;</code></p>
<p><code>123 input_path: &quot;object_detection/data/question_train.record&quot;</code></p>
<p><code>125 label_map_path: &quot;object_detection/data/question_labelmap.pbtxt&quot;</code></p>
<p><code>137 input_path: &quot;object_detection/data/question_val.record&quot;</code></p>
<p><code>139 label_map_path: &quot;object_detection/data/question_labelmap.pbtxt&quot;</code></p>
<p>특히 108줄의 fine_tune 체크포인트 부분을 미리 학습된 모델의 체크포인트파일을 작성해주어야 학습을 연결하여 진행할 수 있습니다 !!
원하는 모델은 <a href="https://github.com/tensorflow/models/blob/r1.12.0/research/object_detection/g3doc/detection_model_zoo.md">여기</a> 에서 다운로드 후 사용가능합니다.</p>
<p><a href="https://github.com/Nayeon12/Scoring-Paper-Tests-with-RCNN/blob/main/model/faster_rcnn_inception_resnet_v2_atrous_coco_choice.config">Faster_RCNN.config</a></p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/230d9899-3812-4df1-be76-38d1bc2a6140/image.png" alt=""></p>
<p>동일한 방식으로 학습을 진행합니다. 챕터 3를 포스팅할때와 최종 학습 step이 조금 바뀐점이 있다면, 많은 수행착오끝에 수능시험지 만을 학습에 사용했고, 학습시 각각 100장 정도의 train set 으로만 진행하였습니다. 또한 문제 감지 모델학습을 위해서는 약 1300 step을, 항목 감지 모델학습을 위해서는 약 1097 step을 학습하였습니다. </p>
<h2 id="모델-테스트">모델 테스트</h2>
<p>모델 테스트 과정입니다. 여기서도 앞서 문제 감지모델의 테스트에서 진행한 과정과 동일하게 진행됩니다. 따라서 그 과정에 필요한 파일에 관한 설명은 아래 링크에서 볼 수 있습니다.</p>
<p><a href="https://velog.io/@nayeon_p00/%EC%A2%85%EC%9D%B4-%EC%8B%9C%ED%97%98%EC%A7%80-%EC%9E%90%EB%8F%99-%EC%B1%84%EC%A0%90-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-Tensorflow-Object-Detection-API-Ch3.5.-%EB%AA%A8%EB%8D%B8-%ED%95%99%EC%8A%B5-%ED%9B%84-frozeninferencegraph.pb-%EB%A1%9C-inference-%ED%95%98%EA%B8%B0">모델 테스트 과정 - Inference</a></p>
<p>여기서 확인한 것은 각 문제에서 선택한 항목과 선택하지 않은 항목을 올바르게 인식하였는지, 다른 색의 bouding box로 시각화하여 확인했습니다. 또한 최종적으로 구현될 프로그램에서 몇번을 선택했는지 파악해야 하므로, 각 문제에서 몇번이 선택되었는지를 확인하는 알고리즘을 구현했습니다.</p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/ec71452d-944a-4208-b53a-e4c90b677966/image.png" alt=""></p>
<p>먼저 위 사진은 모델이 감지한 항목을 시각화한 사진으로, 제대로 인식하는 모습을 확인할 수 있습니다.</p>
<p>각 문제에서 몇번이 선택되었는지를 확인하기 위해서는 수능 시험 객관식의 나열 패턴을 파악해야 했습니다.</p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/bd0c3b31-ce5a-4187-a696-26b8f935e3d9/image.png" alt=""></p>
<p>총 세가지의 패턴이 있습니다. 위 세가지의 패턴 중 각 문제가 어떤 패턴으로 객관식이 구성되어있는지를 먼저 확인하고, 각 패턴에 따라 위치별로 선택한 항목이 몇번째 위치에 있는지 확인하는 것을 구현했습니다. 거창한 알고리즘은 없고, 단순히 위치로만 판단하는 것을 구현했습니다. 모든 코드는 gitHub에 업로드 했습니다.</p>
<p><a href="https://github.com/Nayeon12/Scoring-Paper-Tests-with-RCNN/blob/main/code/Inference_Choice%20and%20Not-choice.ipynb">Choice Number.ipynb</a></p>
<hr>
<p>종이시험지 자동채점 프로그램에 관한 포스팅은 여기서 마칩니다. 자세한 동작영상과 코드는 github에서 확인할 수 있습니다.</p>
<p><a href="https://github.com/Nayeon12/Scoring-Paper-Tests-with-RCNN">https://github.com/Nayeon12/Scoring-Paper-Tests-with-RCNN</a></p>
<p>!youtube[nVRXpLfXRB0]</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[종이 시험지 자동 채점 프로그램 | Tensorflow Object Detection API | Ch3.5. 모델 학습 후 frozen_inference_graph.pb 로 inference 하기 & 모델 테스트]]></title>
            <link>https://velog.io/@nayeon_p00/%EC%A2%85%EC%9D%B4-%EC%8B%9C%ED%97%98%EC%A7%80-%EC%9E%90%EB%8F%99-%EC%B1%84%EC%A0%90-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-Tensorflow-Object-Detection-API-Ch3.5.-%EB%AA%A8%EB%8D%B8-%ED%95%99%EC%8A%B5-%ED%9B%84-frozeninferencegraph.pb-%EB%A1%9C-inference-%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@nayeon_p00/%EC%A2%85%EC%9D%B4-%EC%8B%9C%ED%97%98%EC%A7%80-%EC%9E%90%EB%8F%99-%EC%B1%84%EC%A0%90-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-Tensorflow-Object-Detection-API-Ch3.5.-%EB%AA%A8%EB%8D%B8-%ED%95%99%EC%8A%B5-%ED%9B%84-frozeninferencegraph.pb-%EB%A1%9C-inference-%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 01 Jan 2022 14:48:06 GMT</pubDate>
            <description><![CDATA[<p>모델 테스트를 위해서는 다음과 같은 과정이 필요합니다.</p>
<p><code>추론 그래프 추출</code>
<code>추론 그래프를 사용하여 객체 검출</code></p>
<p>추론그래프를 추출하기 위해서는 Tensorflow object Detection API에서 제공되는 export_inference_graph.py을 사용하면 됩니다.</p>
<h2 id="추론그래프-추출">추론그래프 추출</h2>
<hr>
<p>바로 본론으로 넘어가겠습니다. 챕터3에서 학습을 진행하면 체크포인트가 실행폴더에 저장되게 됩니다. 파일형태는 model.ckpt-xxxx와 같은 형태로 저장되며, 원하는 체크포인트 번호를 명령어에 입력해 추론그래프로 만들어주는 과정이 필요합니다. 명령어 코드는 아래에 작성되어 있으며, 제가 프로젝트를 진행하며 사용한 ipynb 코드는 깃허브를 통해 업로드 하였습니다.</p>
<p><a href="https://github.com/Nayeon12/Scoring-Paper-Tests-with-RCNN/blob/main/code/pb%20file%20creation.ipynb">pb file creation</a></p>
<p><code>! python export_inference_graph.py --input_type image_tensor --pipeline_config_path training/faster_rcnn_inception_resnet_v2_atrous_coco.config --trained_checkpoint_prefix samples/model.ckpt-xxxxx --output_directory inference_graph</code></p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/66d6b890-ebf4-4a99-ad6e-1b5526152c6e/image.png" alt=""></p>
<p>이 과정에서 추론그래프를 생성할 수 있습니다. 추론그래프는 위와 같이 저장됩니다. 그 중 frozen_inference_graph.pb 파일이 모델 테스트를 위해 사용할 추론그래프 파일입니다.</p>
<h2 id="추론-그래프를-이용한-객체-검출">추론 그래프를 이용한 객체 검출</h2>
<hr>
<p>추론그래프를 생성하였다면 test set으로 추론 결과를 시각화 하게 됩니다. </p>
<p><a href="https://github.com/Nayeon12/Scoring-Paper-Tests-with-RCNN/blob/main/code/Inference_Question_Detection.ipynb">Inference Code</a></p>
<p>자세한 코드는 깃허브에 업로드 하였습니다.</p>
<p>추가로 각 코드에 대해 설명하겠습니다.</p>
<pre><code class="language-py">PATH_TO_FROZEN_GRAPH = &#39;inference_graph/frozen_inference_graph.pb&#39;

detection_graph = tf.Graph()
with detection_graph.as_default():

    od_graph_def = tf.compat.v1.GraphDef()

    with tf.compat.v2.io.gfile.GFile(PATH_TO_FROZEN_GRAPH, &#39;rb&#39;) as f:

        serialized_graph = f.read()
        od_graph_def.ParseFromString(serialized_graph)

        tf.import_graph_def(od_graph_def, name = &quot;&quot;)</code></pre>
<blockquote>
<p>먼저 앞에서 생성한 추론그래프를 불러옵니다.
PATH_TO_FROZEN_GRAPH 에서 저장된 추론그래프의 경로를 지정해주면 됩니다.</p>
</blockquote>
<pre><code class="language-py">def run_inference_for_single_image(image, graph):
    with tf.compat.v1.Session(graph = graph) as sess:

        input_tensor = graph.get_tensor_by_name(&#39;image_tensor:0&#39;)

        target_operation_names = [&#39;num_detections&#39;, &#39;detection_boxes&#39;,
                                  &#39;detection_scores&#39;, &#39;detection_classes&#39;, &#39;detection_masks&#39;]
        tensor_dict = {}
        for key in target_operation_names:
            op = None
            try:
                op = graph.get_operation_by_name(key)

            except:
                continue

            tensor = graph.get_tensor_by_name(op.outputs[0].name)
            tensor_dict[key] = tensor

        if &#39;detection_masks&#39; in tensor_dict:
            detection_boxes = tf.squeeze(tensor_dict[&#39;detection_boxes&#39;], [0])

        output_dict = sess.run(tensor_dict, feed_dict = {input_tensor : [image]})

        output_dict[&#39;num_detections&#39;] = int(output_dict[&#39;num_detections&#39;][0])
        output_dict[&#39;detection_classes&#39;] = output_dict[&#39;detection_classes&#39;][0].astype(np.uint8)
        output_dict[&#39;detection_boxes&#39;] = output_dict[&#39;detection_boxes&#39;][0]
        output_dict[&#39;detection_scores&#39;] = output_dict[&#39;detection_scores&#39;][0]

        return output_dict</code></pre>
<blockquote>
<p>run_inference_for_single_image 함수입니다. 이 함수를 통해 output 딕셔너리를 return 하게 되는데, 이것은 하나의 이미지에 대해 객체가 있는 좌표, score, 클래스 number등을 정보에 저장하는 것입니다.</p>
</blockquote>
<pre><code class="language-py">def draw_bounding_boxes(img, output_dict,count):
    boxlist = []
    height, width, _ = img.shape

    obj_index = output_dict[&#39;detection_scores&#39;] &gt; 0.8

    scores = output_dict[&#39;detection_scores&#39;][obj_index]
    boxes = output_dict[&#39;detection_boxes&#39;][obj_index]
    classes = output_dict[&#39;detection_classes&#39;][obj_index]

    for i in range(len(boxes)):
      boxlist.append([boxes[i][0], boxes[i][1], boxes[i][2], boxes[i][3]])

    boxlist.sort(key=lambda x:x[1])
    print(boxlist)

    if(len(boxlist) &gt;= 3):
      if(len(boxlist) == 4):
        if(boxlist[0][0] &gt; boxlist[1][0]):
          boxlist[0], boxlist[1] = boxlist[1], boxlist[0]

        if(boxlist[2][0] &gt; boxlist[3][0]):
          boxlist[2], boxlist[3] = boxlist[3], boxlist[2]
      else:
        if(boxlist[0][0] &gt; boxlist[1][0]):
          boxlist[0], boxlist[1] = boxlist[1], boxlist[0]
    print(boxlist)

    count1 = 0
    for box in boxlist:
        count1 += 1

        cut_img = img[int(box[0] * height):int(box[2] * height), int(box[1] * width):int(box[3] * width)].copy()
        save_img = Image.fromarray(cut_img)
        save_img.save(&quot;data/images_choice/train/cut_jpgs/image_{0}{1}.jpg&quot;.format(count,count1))

    return img</code></pre>
<blockquote>
<p>draw_bounding_boxes 함수입니다. 이것은 모델이 추론한 객체의 부분을 박스로 그려 return 하는 함수인데, 저는 이것에 추가로 박스 모양을 각각 잘라 저장하는 코드를 추가했습니다. 코드의 중간쯤에 if 문으로 작성된 코드는 위치별로 시험지 속 문제들을 순서대로 저장하기 위한 코드이며, 이 코드에 따라 페이지 속 문제들이 번호 순서대로 잘려 저장됩니다. </p>
</blockquote>
<h4 id="cropping-question-by-inferenceipynb"><a href="https://github.com/Nayeon12/Scoring-Paper-Tests-with-RCNN/blob/main/code/Cropping%20Question%20By%20Inference.ipynb">Cropping Question By Inference.ipynb</a></h4>
<p><img src="https://images.velog.io/images/nayeon_p00/post/71a05557-0921-469f-9737-55821ecf4abd/image.png" alt=""></p>
<p>최종적으로 잘려진 이미지들은 위와 같이 디렉토리에 저장되고, 저는 이 이미지에서 항목들을 따로 라벨링하여 학습시켜주었습니다. 해당 내용은 챕터 4에 담겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[종이 시험지 자동 채점 프로그램 | Tensorflow Object Detection API | Faster RCNN | Ch3. 문제 분류 모델 학습하기]]></title>
            <link>https://velog.io/@nayeon_p00/%EC%A2%85%EC%9D%B4-%EC%8B%9C%ED%97%98%EC%A7%80-%EC%9E%90%EB%8F%99-%EC%B1%84%EC%A0%90-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-Tensorflow-Object-Detection-API-Faster-RCNN-Ch3.-%EB%AC%B8%EC%A0%9C-%EB%B6%84%EB%A5%98-%EB%AA%A8%EB%8D%B8-%ED%95%99%EC%8A%B5%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@nayeon_p00/%EC%A2%85%EC%9D%B4-%EC%8B%9C%ED%97%98%EC%A7%80-%EC%9E%90%EB%8F%99-%EC%B1%84%EC%A0%90-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-Tensorflow-Object-Detection-API-Faster-RCNN-Ch3.-%EB%AC%B8%EC%A0%9C-%EB%B6%84%EB%A5%98-%EB%AA%A8%EB%8D%B8-%ED%95%99%EC%8A%B5%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 23 Nov 2021 14:27:06 GMT</pubDate>
            <description><![CDATA[<h2 id="학습을-위한-라벨링-labeling">학습을 위한 라벨링 (Labeling)</h2>
<p>먼저 이번 챕터에서 진행하는 학습은 촬영된 시험지에 몇개의 문제가 있는지, 그 위치를 파악하기 위한 것입니다. 따라서 앞서 만들어낸 데이터에서 문제 위치를 직접 라벨링해야 했고, 처음에는 CVAT라는 라벨링 툴을 활용하여 라벨링을 진행했었습니다. 또한 이 라벨링툴에서는 TFRecord로 바로 변환해주는 기능이 있어 이를 활용하여 학습을 진행했습니다. 그러나 학습 후 예측결과를 inference하는 과정에서 아무것도 detection 되지 않는 모습을 확인할 수 있었고, 이에 대해 어떤 것이 문제인지 살펴보며 약 2주 정도가 지난 것 같습니다. </p>
<p>직접 커스텀데이터를 사용하여 학습을 진행하는 많은 블로그에서는 먼저 각 이미지를 라벨링한 정보를 xml파일로 우선 저장을 하고, 그것을 csv파일로 통합한 뒤, TFRecord파일로 변환하는 과정을 거치고 있었습니다. 조금 번거롭다는 생각이 들었지만, 문제원인을 찾기 위해 이 방법을 진행했고, 빠른 테스트를 위해 3개의 이미지로만 실행해본 결과 무언가 detection 되는 것을 시각화 해낼 수 있었습니다. </p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/55297ebc-3723-4f10-8aea-989f137a5845/image.png" alt=""></p>
<p>따라서 저는 LabelImg 라는 라벨링 툴을 활용하여 각 이미지에 대한 라벨링 정보를 xml파일로 저장하는 과정을 재진행했습니다. </p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/177e5f8d-b8d9-463d-952e-4fc4f5a4062f/image.png" alt=""></p>
<p>또한 이번 라벨링을 새롭게 진행하면서 몇가지 수정사항이 있었습니다.
<code>1. 오지선다형 시험만을 데이터에 포함했습니다.</code> 
<code>- 사지선다형을 제외하여 좀 더 학습 범위를 좁혀 정확도 향상에 집중했습니다.</code>
<code>2. 라벨링하지 않는 서술형이 포함된 이미지를 제외했습니다.</code>
<code>- 아무것도 라벨링되어 있지 않은 이미지를 학습하는 경우에 꽤 있었습니다.</code>
<code>3. 이미지를 회전하는 데이터 증강과정을 제외했습니다.</code>
<code>- 실제 데이터를 segmentation하고, 투시변환을 하는 과정에서 회전된 시험지를 이미 바로잡아주는 과정을 거치게 됩니다. 따라서 학습에 혼동을 주는 회전이미지는 불필요하다 판단했습니다.</code></p>
<p>총 1992장의 데이터로 증강을 완료했고, 이를 6 : 2 : 2 비율로 train : val : test set으로 split했습니다.</p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/f47a3c3a-367c-4f22-ab5f-00170223ab7e/image.png" alt=""></p>
<p>라벨링을 완료하면 xml 파일들을 가지고 다음 단계로 넘어갑니다.</p>
<br/>

<h2 id="xml-to-csv-to-tfrecord">xml to csv to TFRecord</h2>
<p><a href="https://seoftware.tistory.com/108?category=929874">https://seoftware.tistory.com/108?category=929874</a></p>
<p>TFRecord파일을 생성하기 위한 모든 코드는 위 블로그를 참고하였습니다. 모든 코드의 출처가 표시되어있으며 코드를 하나의 주피터 노트북파일로 합쳐 깃허브에 업로드하였습니다.</p>
<p>💥<a href="https://github.com/Nayeon12/Scoring-Paper-Tests-with-RCNN/blob/main/xml%20to%20csv%20to%20tfrecord.ipynb">xml to csv to TFRecord</a></p>
<p><code>! python research/generate_tfrecord.py --csv_input=data/images_new/train/train_labels.csv --output_path=research/object_detection/data/question_train.record --i</code></p>
<p>앞서 생성한 csv파일을 통해 TFRecord파일을 위 한줄코드로 생성할 수 있습니다.</p>
<br/>

<h2 id="모델-학습하기">모델 학습하기</h2>
<p>학습을 진행하기 위해 고민한 시간이 많이 흘렀습니다. 학습은 코랩으로 진행했고, 학습을 위한 디렉토리 구성은 아래와 같습니다.</p>
<hr>
<h3 id="디렉토리-구성">디렉토리 구성</h3>
<p><img src="https://images.velog.io/images/nayeon_p00/post/d994f00d-0d00-4b50-b980-9623cd025dd3/image.png" alt=""></p>
<p>train.py 파일과 object_detection 폴더를 함께 두어야 에러가 나지 않습니다.</p>
<hr>
<h3 id="config-파일-수정사항">config 파일 수정사항</h3>
<p>본격적으로 학습을 하기 전 몇가지 요구사항이 있습니다. 먼저 config파일 속 몇가지를 수정해 실행해야 합니다.</p>
<h4 id="1-num_classes">1. num_classes</h4>
<p><img src="https://images.velog.io/images/nayeon_p00/post/2d219936-96a8-49d7-ab0a-285135dcbd84/image.png" alt=""></p>
<h4 id="2-input_path">2. input_path</h4>
<p><img src="https://images.velog.io/images/nayeon_p00/post/84f521c1-1f45-4e75-9d45-c3b8304e8eb7/image.png" alt=""></p>
<p>아래 있는 eval_input_reader도 마찬가지로 수정합니다.</p>
<hr>
<h3 id="label-map">Label map</h3>
<pre><code class="language-py">item {
    id: 1
    name: &#39;question&#39;
}</code></pre>
<hr>
<h3 id="training">Training</h3>
<p>모듈을 import하고 아래 코드를 순서대로 실행합니다.</p>
<pre><code class="language-py">import numpy as np
import os
import six.moves.urllib as urllib
import sys
import tarfile
import tensorflow as tf
import zipfile

from collections import defaultdict
from io import StringIO
from matplotlib import pyplot as plt

from PIL import Image

from object_detection.utils import label_map_util
from object_detection.utils import visualization_utils as vis_util</code></pre>
<p><code>!protoc object_detection/protos/string_int_label_map.proto --python_out=.</code></p>
<pre><code class="language-py">from google.colab import files
! pip install tensorflow==1.15
!pip install tf_slim
!pip install lvis
!pip install tensorflow-addons
!clear</code></pre>
<p>버전을 다운그레이드해야 에러가 나지 않았습니다.</p>
<p>위 코드를 실행한 후 런타임을 다시시작합니다.</p>
<p><code>!protoc object_detection/protos/*.proto --python_out=.</code>
<code>!export PYTHONPATH=$PYTHONPATH:/content/object_detection</code>
<code>!export PYTHONPATH=$PYTHONPATH:/content/slim/</code></p>
<p>마지막으로 아래 코드를 실행하면 학습이 진행됩니다. </p>
<p><code>!python train.py --train_dir=object_detection/samples/configs/ --pipeline_config_path=object_detection/samples/configs/faster_rcnn_inception_resnet_v2_atrous_coco.config</code></p>
<p>마지막 config 파일을 앞에서 사용하고자 하는 모델의 config파일의 수정본을 넣으면 됩니다.</p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/82414483-3939-4b5b-9544-31e71ebcb938/image.png" alt=""></p>
<p>저는 4500 step까지 학습을 진행했고, 더 이상 하려 해도 코랩으로는 진행할 수 없었습니다.</p>
<p>학습을 진행하며 사용한 모든 코드는 깃허브에 업로드완료 했습니다.</p>
<p>💬<a href="https://github.com/Nayeon12/Scoring-Paper-Tests-with-RCNN/blob/main/Question%20Training%20with%20Faster%20RCNN.ipynb">Training Code</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[종이 시험지 자동 채점 프로그램 | Least Square Method | Perspective Transform | Ch2.5. 시험지를 평면이미지로 변환하기]]></title>
            <link>https://velog.io/@nayeon_p00/%EC%A2%85%EC%9D%B4-%EC%8B%9C%ED%97%98%EC%A7%80-%EC%9E%90%EB%8F%99-%EC%B1%84%EC%A0%90-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-Least-Square-Method-Perspective-Transform-Ch2.5.-%EC%8B%9C%ED%97%98%EC%A7%80%EB%A5%BC-%ED%8F%89%EB%A9%B4%EC%9D%B4%EB%AF%B8%EC%A7%80%EB%A1%9C-%EB%B3%80%ED%99%98%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@nayeon_p00/%EC%A2%85%EC%9D%B4-%EC%8B%9C%ED%97%98%EC%A7%80-%EC%9E%90%EB%8F%99-%EC%B1%84%EC%A0%90-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-Least-Square-Method-Perspective-Transform-Ch2.5.-%EC%8B%9C%ED%97%98%EC%A7%80%EB%A5%BC-%ED%8F%89%EB%A9%B4%EC%9D%B4%EB%AF%B8%EC%A7%80%EB%A1%9C-%EB%B3%80%ED%99%98%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 23 Oct 2021 02:43:35 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://velog.io/@nayeon_p00/series/2021CapstoneDesign">종이 시험지 자동 채점 프로그램 시리즈</a></p>
</blockquote>
<h1 id="마스크-이미지로-부터의-가장자리">마스크 이미지로 부터의 가장자리</h1>
<p>ch2에서 다루었던 segmentation 후 또다른 처리를 진행하기로 했습니다. 많이 기울어진 사진에 대해 투시변화를 통해 펼쳐주는 작업을 진행해 정확도를 높이기로 했습니다.</p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/d567a595-4d17-464a-a0d3-663d36b07535/image.png" alt=""></p>
<p>먼저 위의 이미지에서 왼쪽 이미지는 segmentation 결과 나타나는 출력 이미지 입니다. 여기서 마스크 행렬을 얻을 수 있고 마스크 범위안은 True, 범위 밖 즉 배경으로 인식한 부분은 False 값으로 저장되어 있습니다. 여기서 마스크 범위 가장자리의 종이의 변에 해당하는 부분을 따로 점을 찍어 표현한 것이 오른쪽 입니다. 이제 여기서 각 네변의 직선의 방정식을 구하기 위해 위, 왼쪽, 오른쪽, 아래 총 네 범위로 나누어 점들을 각각 저장해야 합니다.</p>
<h1 id="각-변의-점의-좌표">각 변의 점의 좌표</h1>
<p>마지막에 깃허브를 통해 제가 작성한 코드를 전체 공개할 예정이며, 여기서 사용한 코드를 일부 보여드립니다.</p>
<pre><code class="language-py">## 가장자리 true 값 좌표 알기 / A, B 행렬에 넣기
from matplotlib import pyplot as plt
import numpy as np

upA = np.empty((0,2), int)
upB = np.array([])
rightA = np.empty((0,2), int)
rightB = np.array([])
leftA = np.empty((0,2), int)
leftB = np.array([])
downA = np.empty((0,2), int)
downB = np.array([])

## scatter를 위한 list
#uplistx = []
#uplisty = []
#rightlistx = []
#rightlisty = []
#leftlistx = []
#leftlisty = []
#downlistx = []
#downlisty = [] 
#totalx = []
#totaly = []

for i in range(0, pred_masks.size()[1],2):
  for j in range(0, pred_masks.size()[2],2):
    if(pred_masks[0][i][j] == True):

      if (pred_masks[0][i-1][j] == False and pred_masks[0][i+1][j] == True):
        if(len(upA) == 0 or upA[-1][0] == j):
          upA = np.append(upA, np.array([[j, 1]]), axis=0)
          upB = np.append(upB, np.array([i]))
          #uplistx.append(j)
          #uplisty.append(i)
          #totalx.append(j)
          #totaly.append(i)
          continue

        if(abs((upB[-1] - i) / (upA[-1][0] - j)) &lt; 3):
          upA = np.append(upA, np.array([[j, 1]]), axis=0)
          upB = np.append(upB, np.array([i]))
          #uplistx.append(j)
          #uplisty.append(i)
          #totalx.append(j)
          #totaly.append(i)

          if(len(upA) == 1):
            upA.pop(0)
            upB.pop(0)


      elif (pred_masks[0][i][j+1] == False and pred_masks[0][i][j-1] == True):
        if(len(rightA) == 0 or rightA[-1][0] == j):
          rightA = np.append(rightA, np.array([[j, 1]]), axis=0)
          rightB = np.append(rightB, np.array([i]))
          #rightlistx.append(j)
          #rightlisty.append(i)
          #totalx.append(j)
          #totaly.append(i)
          continue

        if(abs((rightB[-1] - i )/ (rightA[-1][0] - j)) &gt; 6):
          rightA = np.append(rightA, np.array([[j, 1]]), axis=0)
          rightB = np.append(rightB, np.array([i]))
          #rightlistx.append(j)
          #rightlisty.append(i)
          #totalx.append(j)
          #totaly.append(i)

          if(len(rightA) == 1):
            rightA.pop(0)
            rightB.pop(0)


      elif (pred_masks[0][i][j-1] == False and pred_masks[0][i][j+1] == True):
        if(len(leftA) == 0 or leftA[-1][0] == j):
          leftA = np.append(leftA, np.array([[j, 1]]), axis=0)
          leftB = np.append(leftB, np.array([i]))
          #leftlistx.append(j)
          #leftlisty.append(i)
          #totalx.append(j)
          #totaly.append(i)
          continue

        if(abs((leftB[-1] - i )/ (leftA[-1][0] - j)) &gt; 6):
          leftA = np.append(leftA, np.array([[j, 1]]), axis=0)
          leftB = np.append(leftB, np.array([i]))
          #leftlistx.append(j)
          #leftlisty.append(i)
          #totalx.append(j)
          #totaly.append(i)

          if (len(leftA) == 1):
            leftA.pop(0)
            leftB.pop(0)


      elif (pred_masks[0][i+1][j] == False and pred_masks[0][i-1][j] == True):
        if(len(downB) == 0 or downA[-1][0] == j):
          downA = np.append(downA, np.array([[j, 1]]), axis=0)
          downB = np.append(downB, np.array([i]))
          #downlistx.append(j)
          #downlisty.append(i)
          #totalx.append(j)
          #totaly.append(i)

          continue
        if(abs((downB[-1] - i )/ (downA[-1][0] - j)) &lt; 3):
          downA = np.append(downA, np.array([[j, 1]]), axis=0)
          downB = np.append(downB, np.array([i]))
          #downlistx.append(j)
          #downlisty.append(i)
          #totalx.append(j)
          #totaly.append(i)

          if(len(downA) == 1):
            downA.pop(0)
            downB.pop(0)</code></pre>
<p>주석처리 되어 있는 부분은 시각화를 위한 리스트 append 작업이며, 시각화를 원하지 않을 때는 주석처리를 해두어 시간을 절약했습니다.</p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/de62dd70-6791-46e6-9616-4025b34c6f2f/image.png" alt=""></p>
<p>시각화 하게 되면 각 변에 대해 점들이 위와 같이 찍히게 됩니다. 이것을 이미지 픽셀의 x좌표에 대한 직선의 방정식으로 표현해야 합니다.</p>
<h1 id="기울기와-y절편구하기">기울기와 y절편구하기</h1>
<p>이때 사용한 것은 선형대수의 최소제곱법 입니다. </p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/aa49099b-bc86-4aae-a029-3c2ebff3e202/image.png" alt=""></p>
<p>먼저 x값들에 대한 y값을 표현한 선형식들이 도출됩니다.</p>
<p>그것을 AX = B라는 식으로 표현할 수 있게 되고, 여기서 X행렬은 기울기와 y절편이 포함된 행렬입니다. 그 후 양변에 𝐴^𝑇를 곱하여, 좌변에 X만을 남겨 정리하면 아래와 같은 식이 도출됩니다.</p>
<p><code>X = (𝑨^𝑻 𝑨)^(−𝟏) 𝑨^𝑻B</code></p>
<p>이것을 numpy에서 제공하는 linalg의 역행렬과 곱행렬을 계산하는 inv, dot함수를 통해 각 변에 대해 계산을 해주었습니다.</p>
<pre><code class="language-py">import numpy.linalg as lin

X_up = np.linalg.inv(upA.T.dot(upA)).dot(upA.T).dot(upB)
X_right = np.linalg.inv(rightA.T.dot(rightA)).dot(rightA.T).dot(rightB)
X_left = np.linalg.inv(leftA.T.dot(leftA)).dot(leftA.T).dot(leftB)
X_down = np.linalg.inv(downA.T.dot(downA)).dot(downA.T).dot(downB)</code></pre>
<p><img src="https://images.velog.io/images/nayeon_p00/post/bdad3f64-f86b-4b2e-831b-c4fa6d8724d8/image.png" alt=""></p>
<p>구한 기울기와 y좌표를 통해 그래프를 plot하면 위와 같은 그래프를 각각 얻을 수 있고, 이제 이 네개의 직선에 대한 교점을 구해 투시변화를 위한 네 꼭지점을 구하면 됩니다.</p>
<h1 id="교점-네-꼭지점-구하기">교점, 네 꼭지점 구하기</h1>
<p>교점을 구하기 위해서는 겹쳐지는, 교점이 발생하는 두 직선의 방정식을 연립하여 풀어주면 교점의 x좌표와 y좌표를 구할 수 있습니다.</p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/896b367d-951c-4e06-af2d-38897137f3d0/image.png" alt=""></p>
<p>여기서는 이러한 연립방정식을 해결하는 numpy의 linalg solve함수가 있어 이것을 사용하여 해결해 주었습니다.</p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/65160a98-9a7a-4ac1-8926-76396c5ba898/image.png" alt=""></p>
<p>위 그림처럼 각 교점에 대해 solve함수로 모두 구해주었고, 가끔 교점의 좌표가 이미지 픽셀 범위를 벗어나는 경우가 있어, 이 경우에는 0이나 픽셀의 최대값으로 설정해주는 작업을 추가로 진행하였습니다.</p>
<h1 id="perspectivetransform-투시변환">PerspectiveTransform 투시변환</h1>
<p>이제 마지막 단계입니다. 앞에서 구한 네개의 꼭지점, 변환해야할 네 좌표점과, 출력될 이미지의 끝 좌표점을 입력하여 getPerspectiveTransform 함수를 통해 투시변환을 진행해주면 됩니다. </p>
<pre><code class="language-py">from PIL import Image

pts1 = np.float32([itpoint_1, itpoint_2, itpoint_4, itpoint_3])

w1 = abs(itpoint_1[0] - itpoint_2[0])
w2 = abs(itpoint_3[0] - itpoint_4[0])
h1 = abs(itpoint_1[1] - itpoint_3[1])
h2 = abs(itpoint_2[1] - itpoint_4[1])
width = max([w1, w2])
height = max([h1, h2])

pts2 = np.float32([[0,0], [width-1, 0], [width-1, height-1], [0, height-1]])

mtrx = cv2.getPerspectiveTransform(pts1, pts2)

result = cv2.warpPerspective(im, mtrx, (int(width), int(height)))
plt.imshow(result)</code></pre>
<p><img src="https://images.velog.io/images/nayeon_p00/post/298a78e3-28f5-4bfd-ba02-daec7fddf1db/image.png" alt=""></p>
<p>그 결과는 위 이미지와 같이 도출됩니다.</p>
<p>조금더 큰 효과를 테스트 해보기 위해 아래의 사진을 처리해보았습니다.</p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/8f52b8c1-d000-43ca-95fe-2bb0257a5011/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/42b7eb62-caae-4715-9f9e-2bd5f9b4d17e/image.png" alt=""></p>
<p>그 결과는 확실히 기울어진 문제가 조금더 정면으로 변환된 것을 확인할 수 있었고, chapter2에서 진행했던 segmentation 모델에 기울어진 시험지 이미지를 좀더 추가해 학습을 진행한다면 더 좋은 성능을 가질 거라고 생각합니다. 그래서 추후에 이미지를 추가해 학습을 진행할 예정입니다.</p>
<p>감사합니다.</p>
<p>모든 코드는 깃허브에 있습니다.</p>
<p>🎁 <a href="https://github.com/Nayeon12/Scoring-Paper-Tests-with-RCNN/blob/main/Paper%20Perspective%20Transform%20With%20LSM%20and%20Segmentation.ipynb">Paper Perspective Transform With LSM and Segmentation</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Faster R-CNN 정리]]></title>
            <link>https://velog.io/@nayeon_p00/Faster-R-CNN-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@nayeon_p00/Faster-R-CNN-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Mon, 18 Oct 2021 09:19:21 GMT</pubDate>
            <description><![CDATA[<p>이번  캡스톤 디자인 프로젝트에서 오브젝트 디텍션을 위해 사용할 모델인 Faster R-CNN을 사용하기 전, 관련내용에 대해 간단히 정리해보기로 했다.</p>
<blockquote>
<p><img src="https://images.velog.io/images/nayeon_p00/post/c4cd1fea-03c2-41cc-bd2f-059c0a19e503/image.png" alt=""><a href="https://www.youtube.com/watch?v=ZhvU7D_qKO8">참고 영상</a></p>
</blockquote>
<p><a href="https://arxiv.org/pdf/1506.01497.pdf">Faster R-CNN: Towards Real-Time ObjectDetection with Region Proposal Networks</a></p>
<h1 id="faster-r-cnn">Faster R-CNN</h1>
<p><img src="https://images.velog.io/images/nayeon_p00/post/ac7bd626-4a00-4753-95cf-7ce9bd9106cd/image.png" alt=""> </p>
<blockquote>
<p><a href="https://herbwood.tistory.com/10">https://herbwood.tistory.com/10</a></p>
</blockquote>
<p><code>conv layer로부터 이미지가 feature map 생성</code>
<code>RPN에 넣음, fast rcnn에 오브젝트가 있을 것 같다는 제안을 하게 됨</code>
<code>RPN과 Fast RCNN이 convolutional Feature를 공유하도록 네트워크를 쌓음</code></p>
<h3 id="아이디어">아이디어</h3>
<p>기존 Fast- RCNN에서는 ...
selective search 가 독립적으로 존재하므로, 이것에 대한 시간이 추가적으로 걸리게 되었다.</p>
<p>그래서 Faster RCNN 에서는 ...</p>
<ol>
<li>convolutional feature map이 Region Proposal에서도 쓰이도록 함</li>
<li>conv layer에 새로운 layer를 쌓아서 region proposal 과 regression , objectness score 계산을 함께 하도록 함</li>
</ol>
<p>selective search를 대체함으로써 proposal에 10 m/s 소요 -&gt; 매우 빨라짐</p>
<hr>
<h3 id="faster-rcnn-train-과정">Faster Rcnn train 과정</h3>
<p><img src="https://images.velog.io/images/nayeon_p00/post/500c04ce-71da-4205-8348-2966095c92dd/image.png" alt=""></p>
<blockquote>
<p><a href="https://towardsdatascience.com/faster-r-cnn-for-object-detection-a-technical-summary-474c5b857b46">https://towardsdatascience.com/faster-r-cnn-for-object-detection-a-technical-summary-474c5b857b46</a></p>
</blockquote>
<p>input 이미지 - conv layers (VGG- 16)에 패스 - feature map 생성</p>
<p>3x3 sliding window적용 (intermediate layer)시켜 같은 크기의 feature map 생성</p>
<p>1x1 feature map은 하나의 convolutional network 해당위치의 지역적 특성을 축약한 것</p>
<p>여기까지가 RPN학습의 베이스</p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/e13d5b31-6b93-4840-b10d-3f3fd6a29b5a/image.png" alt=""></p>
<blockquote>
<p><a href="https://www.youtube.com/watch?v=ZhvU7D_qKO8">https://www.youtube.com/watch?v=ZhvU7D_qKO8</a></p>
</blockquote>
<p>이후 anchor 와 prodiction 과정으로 나뉘게 된다.</p>
<hr>
<p><img src="https://images.velog.io/images/nayeon_p00/post/a05f1291-7b45-4f8c-83dc-784b48503f67/image.png" alt=""></p>
<blockquote>
<p><a href="https://herbwood.tistory.com/10">https://herbwood.tistory.com/10</a></p>
</blockquote>
<p>Anchor 3x3 적용할 때 중앙에 있는 것 anchor.
모든 anchor별로 k개의 앵커 박스 생성 - scale x ratio</p>
<h3 id="anchor-labeling---objectness-labeling">Anchor labeling - Objectness Labeling</h3>
<p>앵커박스인에 실제 object가 있는 지를 판단한다. 
IoU를 기준으로 
<code>0.7이상 positive</code>
<code>0.3이하 negative</code>
<code>0.3~0.7이거나 이미지 경계 벗어난 경우 invalid</code></p>
<h3 id="bbox-offset-labeling">Bbox offset labeling</h3>
<p><img src="https://images.velog.io/images/nayeon_p00/post/1b837f1c-ace1-4c09-b06d-153bb080508b/image.png" alt=""></p>
<blockquote>
<p><a href="https://www.youtube.com/watch?v=ZhvU7D_qKO8">https://www.youtube.com/watch?v=ZhvU7D_qKO8</a></p>
</blockquote>
<p>앵커박스에 해당하는 ground truth와의 위치적 관계를 labeling
anchor box의 위치적 offset으로 설정</p>
<hr>
<h3 id="prediction-network">Prediction network</h3>
<p>오브젝트가 여기에 있을 것 같다는 것에 대한 네트워크</p>
<h4 id="classification">classification</h4>
<p>여기에 오브젝트가 있다 없다 판단, 모든 픽셀에 대해 앵커 박스들의 오브젝트가 있을 확률을 담는다.</p>
<h4 id="regression">regression</h4>
<p>모든 픽셀에 대해 앵커박스가 예측하는 오브젝트의 위치 저장한다.</p>
<hr>
<p>최종적으로 sampling된 p, n 와 loss function 학습
-&gt; 확률적으로 GT와 IoU가 높은 RP를 만들어냄</p>
<h3 id="학습-과정">학습 과정</h3>
<p>RPN학습 -&gt; 여러 proposal 생성
proposal로 Fast RCNN 학습, 초기화된 CNN다시 사용 
-&gt; fine tuning 
-&gt; 파인튜닝된 프리트레인 네트워크 생성
-&gt; 학습된 이 네트워크를 사용하여 rpn 학습
-&gt; 여기서 부터 conv layer 공유</p>
<p>-&gt; 다시 proposal
-&gt; 파인튜닝
-&gt; 반복</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[종이 시험지 자동 채점 프로그램 | Image Segmentation | Mask R-CNN | Ch2. 이미지에서 배경 자르기]]></title>
            <link>https://velog.io/@nayeon_p00/%EC%A2%85%EC%9D%B4-%EC%8B%9C%ED%97%98%EC%A7%80-%EC%9E%90%EB%8F%99-%EC%B1%84%EC%A0%90-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-Image-Segmentation-Mask-R-CNN-Ch2.-%EC%9D%B4%EB%AF%B8%EC%A7%80%EC%97%90%EC%84%9C-%EB%B0%B0%EA%B2%BD-%EC%9E%90%EB%A5%B4%EA%B8%B0</link>
            <guid>https://velog.io/@nayeon_p00/%EC%A2%85%EC%9D%B4-%EC%8B%9C%ED%97%98%EC%A7%80-%EC%9E%90%EB%8F%99-%EC%B1%84%EC%A0%90-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-Image-Segmentation-Mask-R-CNN-Ch2.-%EC%9D%B4%EB%AF%B8%EC%A7%80%EC%97%90%EC%84%9C-%EB%B0%B0%EA%B2%BD-%EC%9E%90%EB%A5%B4%EA%B8%B0</guid>
            <pubDate>Sun, 10 Oct 2021 15:06:03 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이전 포스팅 &gt;&gt; <a href="https://velog.io/@nayeon_p00/%EC%A2%85%EC%9D%B4-%EC%8B%9C%ED%97%98%EC%A7%80-%EC%9E%90%EB%8F%99-%EC%B1%84%EC%A0%90-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-Data-Augmentation-OpenCV-Ch1.-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A7%91-%EB%B0%8F-%EC%A6%9D%EA%B0%95">Ch1. 데이터 수집 및 증강</a></p>
</blockquote>
<h1 id="1-데이터-준비">1. 데이터 준비</h1>
<h2 id="1-1-데이터-수집">1-1. 데이터 수집</h2>
<p><img src="https://images.velog.io/images/nayeon_p00/post/97e2dbbd-2f5f-417a-8999-53090c147830/%EA%B7%B8%EB%A6%BC1.png" alt=""></p>
<p>ch1에서 수집한 데이터와 별개로, 배경이 포함된 이미지를 수집해야 했습니다. 같은 환경에서 제가 모두 이미지를 촬영하게 되는 것 보다 다른 환경에서 촬영한 이미지로 학습하면 더 유연하게 예측이 이뤄질 것이기 때문에 지인들에게 촬영을 부탁했습니다. 총 62장의 이미지를 수집했습니다. 촬영된 이미지는 수학시험지로 한정하지 않았고, 토익문제지, 에이포용지 필기, 자격증 시험 문제집 등 다양했습니다. </p>
<h2 id="1-2-데이터-라벨링">1-2. 데이터 라벨링</h2>
<p><img src="https://images.velog.io/images/nayeon_p00/post/f244479a-0f12-41c6-9221-c0ecf2e82cf7/image.png" alt=""></p>
<p>수집된 이미지를 직접 라벨링했습니다. 배경과 분리해야할 시험지 범위를 모양대로 잘라 라벨링했으며, class 이름은 paper 로 지정하였습니다. 저는 LabelMe 툴을 사용했으며, 설치 과정은 <a href="https://gjghks.tistory.com/66">블로그</a>를 참고했습니다.</p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/51b6ed7c-15af-46e8-9ac3-363f89e4429b/image.png" alt=""></p>
<p>라벨링 후 json 파일로 각각 저장했습니다.</p>
<hr>
<h1 id="2-mask-r-cnn-학습--예측">2. Mask R-CNN 학습 / 예측</h1>
<h2 id="2-1-라이브러리-설치">2-1. 라이브러리 설치</h2>
<p>저는 Detectron2 라이브러리를 사용하기 위해 먼저 Torchvision을 설치했습니다.</p>
<pre><code class="language-py"># torchvision 설치
!pip install -U torch torchvision
!pip install git+https://github.com/facebookresearch/fvcore.git
import torch, torchvision
torch.__version__</code></pre>
<p>torchvision 설치 후 Detectron2를 설치해주었습니다.</p>
<pre><code class="language-py"># detectron2 original repo clone
%cd /content/drive/My Drive/test-paper detector/detectron2
!git clone https://github.com/facebookresearch/detectron2 detectron2_repo
!pip install -e detectron2_repo</code></pre>
<h2 id="2-2-객체-추가">2-2. 객체 추가</h2>
<p><img src="https://images.velog.io/images/nayeon_p00/post/f69b9e46-e7f3-46ef-9add-f7bcd83dd459/image.png" alt=""></p>
<p>그 다음, detectron2에서 제공되는 객체들에서 추가로 제가 설정한 시험지 객체를 추가하기 위해 register_coco_instances()
함수를 사용해 &quot;test-paper&quot;라는 객체를 추가했습니다.</p>
<pre><code class="language-py"># coco 데이터셋에 paper 객체instance를 추가
from detectron2.data.datasets import register_coco_instances
#register_coco_instances(&quot;test-paper&quot;, {}, &quot;./data/trainval.json&quot;, &quot;./data/segmentation_images&quot;)
paper_metadata = MetadataCatalog.get(&quot;test-paper&quot;)
dataset_dicts = DatasetCatalog.get(&quot;test-paper&quot;)</code></pre>
<p>이때, train data set의 통합 json 파일이 필요하며, 원본이미지를 함께 등록해야합니다.</p>
<p>추가 후 아래 코드를 통해 라벨링 상태를 시각화하여 확인해볼 수 있습니다.</p>
<pre><code class="language-py">import random

for d in random.sample(dataset_dicts, 3):

    img = cv2.imread(d[&quot;file_name&quot;])
    visualizer = Visualizer(img[:, :, ::-1], metadata=paper_metadata, scale=0.5)
    vis = visualizer.draw_dataset_dict(d)
    cv2_imshow(vis.get_image()[:, :, ::-1])</code></pre>
<p><img src="https://images.velog.io/images/nayeon_p00/post/c5b70783-6074-4daf-8035-2c76162785d0/image.png" alt=""></p>
<h2 id="2-3-모델-학습">2-3. 모델 학습</h2>
<p>저는 수집한 62장의 이미지 중 test set 7장을 제외한 총 55장을 학습하였습니다. </p>
<pre><code>from detectron2.engine import DefaultTrainer
from detectron2.config import get_cfg
import os

cfg = get_cfg()
cfg.merge_from_file(&quot;./detectron2_repo/configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml&quot;)
cfg.DATASETS.TRAIN = (&quot;test-paper&quot;,)
cfg.DATASETS.TEST = () 
cfg.DATALOADER.NUM_WORKERS = 2
cfg.MODEL.WEIGHTS = &quot;detectron2://COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x/137849600/model_final_f10217.pkl&quot; 
cfg.SOLVER.IMS_PER_BATCH = 2
cfg.SOLVER.BASE_LR = 0.02
cfg.SOLVER.MAX_ITER = 300 
cfg.MODEL.ROI_HEADS.BATCH_SIZE_PER_IMAGE = 128
cfg.MODEL.ROI_HEADS.NUM_CLASSES = 1 

os.makedirs(cfg.OUTPUT_DIR, exist_ok=True)
trainer = DefaultTrainer(cfg)
trainer.resume_or_load(resume=False)
trainer.train()</code></pre><p>Detectron2에서 제공하는 미리 학습된 Mask-RCNN모델을 불러와 사용해 주었습니다. 탐지하고자 하는 test-paper 객체만 예측하도록 하기 위해 NUM_CLASSES 는 1로 설정해주었습니다. </p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/3e540abb-c897-4345-af99-2ab9b3a208f5/image.png" alt=""></p>
<h2 id="2-4-예측-결과">2-4. 예측 결과</h2>
<p>제외해두었던 7장으로 모두 테스트 해보았습니다. </p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/38562f75-5166-4378-a3e7-0f3f8e6f27db/%EA%B7%B8%EB%A6%BC2.png" alt=""></p>
<p>색으로 칠해진부분이 모델을 통해 예측된 segmentation 이미지 입니다. 적은 데이터 개수로 학습을 진행했지만 미리 학습된 모델이라 결과는 생각보다 잘 나온 것 같습니다. 프로젝트를 진행하며 모델에 대한 데이터 셋은 더 추가할 예정입니다. </p>
<pre><code>from detectron2.engine import DefaultTrainer
from detectron2.config import get_cfg
cfg.MODEL.WEIGHTS = &quot;/content/drive/My Drive/test-paper detector/detectron2/output/model_final.pth&quot; 
cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = 0.5 
cfg.DATASETS.TEST = (&quot;test-paper&quot;, )
predictor = DefaultPredictor(cfg)

path = &quot;/content/drive/My Drive/test-paper detector/detectron2/data/segmentation_images/test/0062_seg.jpg&quot;

im = cv2.imread(path)
outputs = predictor(im)
v = Visualizer(im[:, :, ::-1], MetadataCatalog.get(cfg.DATASETS.TRAIN[0]), scale=1.2)
v = v.draw_instance_predictions(outputs[&quot;instances&quot;].to(&quot;cpu&quot;))
cv2_imshow(v.get_image()[:, :, ::-1])</code></pre><hr>
<h1 id="3-segmentation-mask-자르기">3. segmentation mask 자르기</h1>
<p>이제 원본이미지를 예측된 segmentation mask를 따라 잘라보겠습니다. 이 과정은 실제 데이터를 입력했을 때 배경과 시험지를 잘라 정확도를 더 높이기 위한 과정입니다.</p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/810ef6cd-eb7e-4249-9f55-3e38d03e7797/%EA%B7%B8%EB%A6%BC3.png" alt=""></p>
<p>위 이미지는 test set 7장 중 하나이며, 오른쪽 사진은 segmentation 예측 결과입니다. segmentation 결과는 mask 행렬로서 output 객체에 저장됩니다. 따라서 저는 이 mask 행렬을 활용하여 이미지를 잘라보았습니다. </p>
<pre><code class="language-py">import matplotlib.pyplot as plt 
from matplotlib import cm
ins = outputs[&quot;instances&quot;]
pred_masks = ins.get_fields()[&quot;pred_masks&quot;]
#boxes = ins.get_fields()[&quot;pred_boxes&quot;]    
pred_masks</code></pre>
<p>outputs의 &quot;instances&quot;가 존재합니다. 그 속에는 예측된 마스크 행렬을 포함하는 pred_masks와, 대상 객체가 존재하는 위치를 저장해둔 pred_boxes가 있습니다. 저는 pred_masks을 불러왔습니다. </p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/10104e54-392a-4bbc-bac3-d145198250f0/image.png" alt=""></p>
<p>그 속에는 False와 True로 가득찬 행렬이 존재하고 tensor 타입 행렬을 numpy로 우선 바꾸어 주었습니다. </p>
<pre><code class="language-py">## tensor to numpy
mask_array = pred_masks.cpu().numpy()</code></pre>
<p>그 다음, numpy 행렬을 이미지로 바꾸기 위해 fromarray 함수를 사용하게 되면 아래와 같은 마스크 이미지가 도출됩니다.</p>
<pre><code class="language-py">## numpy to image
mask = Image.fromarray(mask_array[0])</code></pre>
<p><img src="https://images.velog.io/images/nayeon_p00/post/e5139176-708b-4bac-ba1b-47f978a3e206/image.png" alt=""></p>
<p>이 마스크 이미지를 가지고 bitwise_or 연산을 시행해줍니다. 시행 전 </p>
<p><code>img = 255-img</code></p>
<p>을 통해 이미지 반전을 해주고, </p>
<pre><code class="language-py">img = (img*1).astype(&#39;uint8&#39;)
masked = cv2.bitwise_or(img,im)</code></pre>
<p>원본이미지와 마스크 이미지 bitwise_or 연산을 시행하면, 원본이미지에서 마스크이미지가 잘려진 형태의 이미지가 저장됩니다.</p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/9cbd5edf-d204-4e34-b07b-df29eb8de35b/image.png" alt=""></p>
<p>이제 이 이미지를 가지고 더 높은 정확도의 예측이 추후에 가능하게 되었습니다.</p>
<p>사용한 모든 코드는 깃허브에 업로드하였습니다.
감사합니다.</p>
<p><a href="https://github.com/Nayeon12/Scoring-Paper-Tests-with-RCNN">💾 Scoring-Paper-Tests-with-RCNN</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[종이 시험지 자동 채점 프로그램 | Data Augmentation | OpenCV | Ch1. 데이터 수집 및 증강]]></title>
            <link>https://velog.io/@nayeon_p00/%EC%A2%85%EC%9D%B4-%EC%8B%9C%ED%97%98%EC%A7%80-%EC%9E%90%EB%8F%99-%EC%B1%84%EC%A0%90-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-Data-Augmentation-OpenCV-Ch1.-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A7%91-%EB%B0%8F-%EC%A6%9D%EA%B0%95</link>
            <guid>https://velog.io/@nayeon_p00/%EC%A2%85%EC%9D%B4-%EC%8B%9C%ED%97%98%EC%A7%80-%EC%9E%90%EB%8F%99-%EC%B1%84%EC%A0%90-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-Data-Augmentation-OpenCV-Ch1.-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A7%91-%EB%B0%8F-%EC%A6%9D%EA%B0%95</guid>
            <pubDate>Thu, 07 Oct 2021 13:52:25 GMT</pubDate>
            <description><![CDATA[<h1 id="1-데이터-수집">1. 데이터 수집</h1>
<p>데이터는 모두 수학시험을 대상으로 수집하기로 했습니다. 모두 객관식으로 이루어진 시험지를 수집했으며, 수집한 데이터는 다음과 같습니다.</p>
<blockquote>
<p><a href="https://police.ac.kr/police/police/master/62/boardList.do?mdex=police69">경찰 대학 시험 (수학) </a>2014~ 
<a href="https://www.ebs.co.kr/public/9/examarchive/question">9급 공무원 시험 (수학)</a> 2013~
<a href="https://www.kice.re.kr/boardCnts/list.do?boardID=1500234&amp;m=0403&amp;s=suneung&amp;searchStr">수능 시험 (수학)</a> 2014~
<a href="https://www.kice.re.kr/boardCnts/list.do?boardID=1500236&amp;m=0403&amp;s=suneung">모의고사 시험 (수학)</a> 2019~
<a href="https://www.kice.re.kr/boardCnts/list.do?boardID=1500211&amp;s=kice&amp;searchStr=&amp;m=030305">검정고시 초, 중, 고 시험 (수학) </a>2017~
<a href="http://www.0-buhaza.com/bbs/board.php?bo_table=hak_m2_2013&amp;sca=%BC%F6%C7%D0&amp;page=7&amp;wr_1=&amp;sca=%BC%F6%C7%D0&amp;page=7&amp;wr_2=&amp;sca=%BC%F6%C7%D0&amp;page=7&amp;wr_4=&amp;sca=%BC%F6%C7%D0&amp;page=7&amp;page=9">중학교 중간, 기말고사 </a>(수학)</p>
</blockquote>
<p><img src="https://images.velog.io/images/nayeon_p00/post/9684b5aa-647b-4f94-8da8-3702fbd3536d/image.png" alt=""></p>
<p>모두 pdf 로 저장하였으며 푼 상태의 시험지가 필요했습니다. 그러나 수집한 데이터는 모두 원본 상태이며, 임의로 체크표시와 밑줄 등을 통해 푼 시험지를 재현했습니다. </p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/2ccf8784-ddf9-4efe-9982-25f61a36d26e/image.png" alt=""></p>
<p>그 후 각 페이지 당 jpg 파일로 저장했으며 총 395장의 데이터가 수집되었습니다. </p>
<hr>
<h1 id="2-데이터-증강">2. 데이터 증강</h1>
<h2 id="2-1-밝기">2-1. 밝기</h2>
<p>수집된 데이터를 직접 찍은 이미지와 비슷한 조건으로 만들기 위해 다양한 방법으로의 데이터 변환이 필요 했습니다. 먼저, 다양한 환경의 조명들을 고려해 밝기를 조절하였습니다. </p>
<pre><code class="language-py">from matplotlib import pyplot as plt
from PIL import Image
import cv2
import numpy as np
import random
import os
import PIL.ImageOps

COUNT = 1

for i in range(len(images)):
  image = cv2.imread(images[i])
  val = random.randrange(10,100)
  array = np.full(image.shape, (val, val, val), dtype=np.uint8)

  sub_dst = cv2.subtract(image, array)

  #plt.imshow(sub_dst)
  bright_image = Image.fromarray(sub_dst, &#39;RGB&#39;)
  bright_image.save(&quot;/content/drive/MyDrive/test-paper detector/detectron2/data/images/image_bright_%03d.jpg&quot; % COUNT)
  images.append(&quot;/content/drive/MyDrive/test-paper detector/detectron2/data/images/image_bright_%03d.jpg&quot; % COUNT)
  COUNT += 1</code></pre>
<p><img src="https://images.velog.io/images/nayeon_p00/post/c1fee6bf-4496-46b2-86a4-c47c80d5a7b1/image.png" alt=""></p>
<p>openCV의 subtract 함수를 통해 픽셀값을 빼주는 방식으로 밝기를 조절했습니다. 수집된 원본 이미지는 이미 밝기가 밝으므로 어둡게만 처리해주었습니다. 랜덤으로 10에서 100 사이 값을 불러와 그만큼 픽셀값에서 빼주었습니다. </p>
<p>또한 numpy array 를 이미지로 변환하여 저장하는 과정을 거쳤습니다. </p>
<br/>

<h2 id="2-2-회전">2-2. 회전</h2>
<pre><code class="language-py">rotate_count = 1

for i in range(len(images)):
  image = Image.open(images[i])
  rotated_image = image.rotate(random.randrange(-15, 15)) ## 랜덤하게 -15도 ~ +15도
  #plt.imshow(rotated_image)
  rotated_image.save(&#39;/content/drive/MyDrive/test-paper detector/detectron2/data/images/image_rotate_%03d.jpg&#39; % rotate_count)
  images.append(&#39;/content/drive/MyDrive/test-paper detector/detectron2/data/images/image_rotate_%03d.jpg&#39; % rotate_count)
  rotate_count += 1</code></pre>
<p><img src="https://images.velog.io/images/nayeon_p00/post/adac0334-b9f6-4f8a-a0e8-f882bb6e8268/image.png" alt=""></p>
<p>이때는 rotate 함수를 사용하였습니다. 회전 정도를 나타내는 각도를 지정할때는 랜덤으로 -15도 에서 +15도를 불러와 회전시켜주었습니다. </p>
<br/>

<h2 id="2-3-y축-비틀기">2-3. y축 비틀기</h2>
<pre><code class="language-py">warp_count = 1

for i in range(len(images)):
  img = cv2.imread(images[i])
  h,w = img.shape[:2]

  pts1 = np.float32([[0, 0], [595, 0], [0, 842], [595, 842]])
  rn = random.randrange(20, 50)

  pts2 = np.float32([[rn, 0], [595-rn, 0], [0, 842], [595, 842]]) ## random 각도

  M = cv2.getPerspectiveTransform(pts1, pts2)
  img2 = cv2.warpPerspective(img, M, (w, h))

  #plt.imshow(img)
  #plt.imshow(img2)
  img2 = Image.fromarray(img2, &#39;RGB&#39;)
  img2.save(&#39;/content/drive/MyDrive/test-paper detector/detectron2/data/images/image_warp_%03d.jpg&#39; % warp_count)
  images.append(&#39;/content/drive/MyDrive/test-paper detector/detectron2/data/images/image_warp_%03d.jpg&#39; % warp_count)
  warp_count += 1
</code></pre>
<p><img src="https://images.velog.io/images/nayeon_p00/post/bc1dc992-3fa5-41a3-82e4-7f1e2a468967/image.png" alt=""></p>
<p>이 과정은 이번 데이터 증강에서 꽤 중요하다고 생각합니다. 사용자가 시험지를 촬영할 때 완전히 위에서 아래를 찍는 것이 아닌 살짝 카메라 화면이 사용자를 향하는 방향으로 촬영하기 때문에 y축 비틀기 과정은 반드시 필요했습니다. </p>
<p>이때는 getPerspectiveTransform 함수와 warpPerspective 함수를 사용했습니다. 원본 이미지의 가장자리 꼭지점 좌표와 변환하고자 하는 모양의 꼭지점 좌표를 통해 변환 행렬을 구하게 됩니다. 그러면 그 행렬에 대한 기하학적 변화를 수행할 수 있게 됩니다. </p>
<p>여기서 또한 기울어진 정도를 랜덤으로 불러와 시행했으며 20에서 50사이의 값을 불러와 가장 위 쪽 두개의 좌표를 생성했습니다. </p>
<br/>

<h2 id="2-4-노이즈-추가">2-4. 노이즈 추가</h2>
<pre><code class="language-py">noisy_count = 1
 in range(len(images)):

for i
  img = cv2.imread(images[i])
  row,col,ch= img.shape

  mean = 0
  var = 0.1
  sigma = var**0.5
  gauss = np.random.normal(mean,sigma,(row,col,ch))
  gauss = gauss.reshape(row,col,ch)

  noisy_array = img + gauss
  #plt.imshow(noisy_array)
  noisy_image = Image.fromarray(np.uint8(noisy_array)).convert(&#39;RGB&#39;)
  noisy_image.save(&#39;/content/drive/MyDrive/test-paper detector/detectron2/data/images/image_noise_%04d.jpg&#39; % noisy_count)
  images.append(&#39;/content/drive/MyDrive/test-paper detector/detectron2/data/images/image_noise_%04d.jpg&#39; % noisy_count)
  noisy_count += 1</code></pre>
<p><img src="https://images.velog.io/images/nayeon_p00/post/950568b6-3613-4b39-a116-64d4e7e23783/image.png" alt=""></p>
<p>마지막 단계인 노이즈 추가입니다. 지금까지 변환한 모든 사진들에 노이즈를 추가하여 저장하게 됩니다. 이때는 가우시안 필터를 사용했고, 원본 이미지에 가우시안 필터를 더하는 방식으로 진행하였습니다. </p>
<hr>
<p>약 6300장의 이미지가 저장되었으며 직접 찍은 데이터를 대신해 다양한 환경이 적용된 사진이 마련되었습니다. </p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/42b04ef2-fec7-43a2-a079-a668377924d9/image.png" alt=""></p>
<p>사용한 모든 코드는 깃허브에 ipynb 파일로 정리되어있습니다.</p>
<p><a href="https://github.com/Nayeon12/Scoring-Paper-Tests-with-RCNN/blob/main/Data%20Augmentation.ipynb">모든 코드</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Segmentation과 RCNN 모델을 통한 종이 시험지 자동 채점 프로그램 | Tensorflow Object Detection API | Ch0. 계획]]></title>
            <link>https://velog.io/@nayeon_p00/Segmentation%EA%B3%BC-RCNN-%EB%AA%A8%EB%8D%B8%EC%9D%84-%ED%86%B5%ED%95%9C-%EC%A2%85%EC%9D%B4-%EC%8B%9C%ED%97%98%EC%A7%80-%EC%9E%90%EB%8F%99-%EC%B1%84%EC%A0%90-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-Tensorflow-Object-Detection-API-Ch0.-%EA%B3%84%ED%9A%8D</link>
            <guid>https://velog.io/@nayeon_p00/Segmentation%EA%B3%BC-RCNN-%EB%AA%A8%EB%8D%B8%EC%9D%84-%ED%86%B5%ED%95%9C-%EC%A2%85%EC%9D%B4-%EC%8B%9C%ED%97%98%EC%A7%80-%EC%9E%90%EB%8F%99-%EC%B1%84%EC%A0%90-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-Tensorflow-Object-Detection-API-Ch0.-%EA%B3%84%ED%9A%8D</guid>
            <pubDate>Wed, 06 Oct 2021 07:28:12 GMT</pubDate>
            <description><![CDATA[<p>위 사진은 시리즈 배경 등록을 위한 사진입니다.</p>
<h1 id="시작하며">시작하며</h1>
<h2 id="배경">배경</h2>
<p>현재 저는 중학생 과외를 진행하고 있으며, 교육과 시험에 관심이 자연스럽게 많아지며 교육분야에서 부족한 부분이 무엇인지 생각해보았습니다. 온라인 교육을 통해 모바일로 문제를 묻고 답하는 플랫폼이 있는가 하면, 태블릿 pc 등을 통해 문제를 풀고 쉽게 채점하도록 하는 공부 어플들이 매우 많이 있었습니다. 
<br/>
여기서 저는 너무 온라인을 통한 교육만을 추구하고 있는 것은 아닌지, 아직까지는 종이 시험지를 통해 시험을 치고 문제를 푸는 개인과 학원과 같은 단체가 대부분일 것이란 것을 짚고 넘어가야한다고 생각했습니다. 그러나 현재 온라인을 통한 교육이 매우 편리하고 점점 더 많아지는 추세라는 것은 부정할 수 없기 때문에, 종이 시험지를 통한 교육방식에서 어떤 것이 추가가 되면 더 편리해질지를 생각해보았습니다.</p>
<br/>

<h2 id="주제">주제</h2>
<p>종이시험지를 직접 손으로 매기지 않고 한번에 인식하여 어떤 답을 선택했는지를 알려주는 프로그램을 만든다면, 기기를 통해 문제를 풀지 않아도 자동으로 채점해줄 수 있게 됩니다. 그래서 저는 이번 캡스톤 디자인 수업을 통해 <span style="color:purple"><strong>종이 시험지 자동 채점 프로그램</strong></span>을 만들기로 했습니다.</p>
<blockquote>
<p><strong>Flow</strong></p>
<ul>
<li>다 푼 종이 시험지 찍어 프로그램에 입력합니다.</li>
<li>각 문제에 대해 사용자가 몇번을 선택했는지 출력합니다.</li>
<li>정답과 맞는지 확인합니다.</li>
</ul>
</blockquote>
<hr>
<h1 id="데이터-정의-및-준비">데이터 정의 및 준비</h1>
<h2 id="데이터-수집">데이터 수집</h2>
<p>풀려 있는 시험지 데이터가 필요합니다. 사용자는 자신이 푼 시험지를 직접 찍어 업로드 하므로, 학습데이터는 다양한 밝기과 각도, 노이즈가 설정되어 있어야 합니다.</p>
<h2 id="데이터-늘리기">데이터 늘리기</h2>
<p>수집하게 될 데이터는 pdf 파일로, 밝기나 각도의 영향을 받지 않은 매우 깨끗한 시험지 데이터 입니다. 따라서 data augmentation 을 통해 각도와 밝기를 조절하고, 노이즈 필터를 통해 학습데이터를 증강합니다.</p>
<h2 id="데이터-정의분류단계--라벨링">데이터 정의(분류단계) / 라벨링</h2>
<p>새롭게 정의한 주제이기 때문에 분석 목표에 맞게 직접 라벨링해야합니다. CVAT 와 Labelme 툴을 사용할 예정입니다.</p>
<h3 id="분류-1단계">분류 1단계</h3>
<p>문제 분류 : 이미지 데이터 속 문제가 어떤 위치에 있는지 네모 박스로 라벨링 합니다.</p>
<h3 id="분류-2단계">분류 2단계</h3>
<p>항목 분류 : 선택되지 않은 답과 선택한 답 두개의 클래스로 항목을 라벨링합니다. </p>
<hr>
<h1 id="모델-학습-과정">모델 학습 과정</h1>
<h2 id="span-stylecolorgreen문제-분류하기span"><span style="color:green">문제 분류하기</span></h2>
<p><img src="https://images.velog.io/images/nayeon_p00/post/0b296cbd-c6b9-48cc-ae80-00f0b0e52d24/%EA%B7%B8%EB%A6%BC1.png" alt=""></p>
<p>앞서 라벨링 했던 이미지를 TFRecord 파일로 저장합니다. 이는 Tensorflow를 통한 모델 학습을 위한 시간 절감과 효율성을 위한 것입니다. 또한 라벨을 정의하기 위해 pbtxt 파일로 Labelmap을 함께 저장해둡니다.</p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/862f19f7-0815-46b6-8578-4f2db61eed6d/image.png" alt=""></p>
<p>이 두개의 파일을 통해 <span style="color:blue">Tensorflow Object Detection API</span>를 통해 미리 학습된 모델을 사용하여 학습합니다. 자세한 내용은 추후에 실제로 구현하게 될 때 작성하겠습니다.</p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/2acd63f2-7a12-4800-bd12-d01fae94e6f9/image.png" alt=""></p>
<br/>

<h2 id="span-stylecolorgreen항목-분류하기span"><span style="color:green">항목 분류하기</span></h2>
<p><img src="https://images.velog.io/images/nayeon_p00/post/8ef30cae-d5ea-4699-bbac-14f6ce3a7305/%EA%B7%B8%EB%A6%BC2.png" alt=""></p>
<p>선택하지 않은 항목과 선택한 항목을 분류하는 과정입니다. 이것 또한 앞서 라벨링 된 이미지를 TFRecord와 pbtxt로 저장하여 라벨링 범위와 클래스를 정의합니다. </p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/4aa1270e-181c-4cc5-8e45-3fd175241b4b/image.png" alt=""></p>
<p>이때는 두개의 클래스로 학습이 진행되며 문제 분류하기 단계에서와 동일하게 Tensorflow Object Detection API에서 제공되는 미리 학습된 모델을 사용할 계획입니다.</p>
<hr>
<h1 id="모델-평가-과정">모델 평가 과정</h1>
<p>학습과정에서의 두단계에서 각각 다른 모델을 사용하고 fit하게 되므로 따로 모델을 평가하게 됩니다. detection 된 상자 안이 얼마나 실제 범위와 겹쳐졌는지, 그 정도를 파악하여 정확도를 확인합니다. 그 정도를 퍼센트(%)로 나타내고 임계값에 따라 TP, FN, FP를 확인하게 됩니다. 그것을 통해 Precision 과 Recall 를 사용해 정확도를 파악할 예정입니다.</p>
<blockquote>
<p><img src="https://images.velog.io/images/nayeon_p00/post/770cfa99-3ffd-496a-887f-ac36a356ae60/image.png" alt=""><img src="https://images.velog.io/images/nayeon_p00/post/25b2f707-b2ae-4ee5-833f-199d777418c4/image.png" alt=""></p>
</blockquote>
<p>예를 들어, 학습과정에서의 두번째 단계였던 항목을 분류하는 단계에서는, 모델이 얼마나 선택한 답을 많이 골라냈는 지 보다, 선택한 답이라고 한 것 중 실제로 선택한 답인 것이 더 중요하기 때문에 Recall를 사용한다거나 하는 방식입니다.</p>
<hr>
<h1 id="모델-예측-과정">모델 예측 과정</h1>
<p><img src="https://images.velog.io/images/nayeon_p00/post/390d6960-d4d2-4154-9542-5c74230f93b2/image.png" alt=""></p>
<p>먼저 사용자가 채점할 시험지를 촬영해 입력합니다.</p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/fa88f0b9-ee65-4c1c-ac83-6980897288a8/%EA%B7%B8%EB%A6%BC3.png" alt=""></p>
<p>그다음, 더 높은 정확도로 문제를 검출하기 위해 배경과 분리하는 작업을 거칩니다. 이 단계에서 segmentation을 사용할 예정이며, 앞에서 사용했던 네모 범위 detection과 달리 물체의 윤곽선을 따라 검출하므로 배경을 좀 더 세밀하게 crop하기에 용이합니다. </p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/f154d66a-56b8-49f1-bc42-9e4b6096e4d1/image.png" alt=""></p>
<p>bounding box가 아닌, 문제지 상황과 크기에 맞춰 다각형으로 라벨링 하게 되며, 이것을 Pytorch 에서 제공하는 Detectron2를 통해 학습하게 됩니다. 이때 사용할 모델은 Mask R-CNN 이며 이를 통해 마스크 이미지를 얻어 이미지를 잘라내게 됩니다. 자세한 내용은 실제 구현 단계에서 작성하겠습니다. </p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/d0cc2e00-9238-4c69-93d7-681a19ea2b1c/%EA%B7%B8%EB%A6%BC4.png" alt=""></p>
<p>배경을 잘라낸 후 앞에서 학습된 문제 분류 모델을 통해 예측을 진행합니다. </p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/49275cbf-6576-4dbf-9b7a-5cd6bef43c50/%EA%B7%B8%EB%A6%BC5.png" alt=""></p>
<p>감지된 문제를 자르게 되고, 문제들에 대한 번호를 매기기 위해 좌표로 순서를 파악하게 됩니다. 이 프로그램에서 대상으로 하는 것은 시험지 이므로 두단락, 세로로 구성되어 있음을 고려하여 파악합니다. </p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/a166ad0a-782c-4d2b-870d-9aedc6decb41/%EA%B7%B8%EB%A6%BC6.png" alt=""></p>
<p>자른 문제들을 각각 확인하며 항목 분류 모델을 통해 예측하게 되고, 위 그림과 같이 선택한 답과 선택하지 않은 답 두가지로 각 항목을 분류하게 됩니다. 이 것 또한 좌표를 통한 위치를 파악하여 각 box가 몇번 항목을 의미하는 지를 파악합니다. </p>
<p>그러나 이때는, 문제와 다르게 총 네가지 패턴을 가지게 됩니다. 첫째로, 항목이 가로로 쭉 나열된 경우와, 두번째로, 세로로 쭉 나열된 경우, 세번째로 두줄로 항목들이 나열된 경우, 마지막으로 세줄로 항목들이 나열된 경우입니다. 이 네가지 패턴 중 어떤 것에 해당하는 지를 이 단계에서 파악해야 하며 그것을 파악하는 알고리즘을 제대로 개발하는 것이 이번 프로젝트의 완성도를 높이기 위한 핵심이라 생각합니다.</p>
<p><img src="https://images.velog.io/images/nayeon_p00/post/f5cf5507-12a6-40a5-bcfb-7bedc5520405/%EA%B7%B8%EB%A6%BC7.png" alt="">
<img src="https://images.velog.io/images/nayeon_p00/post/d9892997-ba33-4655-b961-b11f6f104092/%EA%B7%B8%EB%A6%BC8.png" alt=""></p>
<p>마지막으로 각 문제 별로 어떤 답을 선택했는지 출력을 하게 되고 프로그램을 종료됩니다. </p>
<hr>
<h1 id="마치며">마치며</h1>
<p>처음으로 해야할 과제는 데이터를 수집하는 것이며, 앞으로의 수행 과정을 블로그에 업로드 하며 정리해나갈 예정입니다. </p>
]]></description>
        </item>
    </channel>
</rss>