<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jyoung_710.log</title>
        <link>https://velog.io/</link>
        <description>세상은 공평하지 않다. 그러나 이것이 우리가 더욱 노력할 기회를 준다.</description>
        <lastBuildDate>Thu, 19 Sep 2024 08:07:46 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jyoung_710.log</title>
            <url>https://velog.velcdn.com/images/jyoung_710/profile/93894d2d-0173-4a17-bd96-7d3784672b52/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jyoung_710.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jyoung_710" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Backpropagation]]></title>
            <link>https://velog.io/@jyoung_710/Backpropagation</link>
            <guid>https://velog.io/@jyoung_710/Backpropagation</guid>
            <pubDate>Thu, 19 Sep 2024 08:07:46 GMT</pubDate>
            <description><![CDATA[<p>우리는 손실함수를 최소화하기 위해 경사하강법을 사용하고, 경사하강법을 통해 가중치를 업데이트하기 위해서는 손실함수의 미분계수가 필요함. 이때 우리가 일반적으로 사용하는 수치 미분을 사용하여 미분계수를 구할 경우, 각 변수를 모두 미분해야 하므로 계산량이 많아지고 이는 너무나 비효율적인 방법임. </p>
<p>따라서 <span style="background-color:#D3E3F5; font-weight: bold;">연쇄 법칙(Chain Rule)를 기반으로 한 역전파(Backpropagation)</span>를 사용하게 됨.</p>
<h1 id="span-stylebackground-colore6e6fabackpropagation-in-dnnspan"><span style="background-color:#E6E6FA">Backpropagation in DNN</span></h1>
<p>아래와 같은 DNN의 상황에서 Backpropagation을 계산해보겠음.
초록색 노드는 bias를 더해주는 node임.
<img src="https://velog.velcdn.com/images/jyoung_710/post/737d52bc-72d3-4535-8506-065431e73c85/image.png" alt="">
우선 사용할 기호들에 대해 아래와 같이 정의하겠음.</p>
<blockquote>
<p>$x_n^{l+1}=g(s_n^l)$
$g(x) : activation function$
$s_n^l$ : l번째 layer에서 l+1번째 layer의 n번째 node로 보내는 값
$W_{ab}^l$ : l번째 layer의 a번째 node에서 l+1번째 layer의 b번째 node로 보낼 때의 가중치</p>
</blockquote>
<p>따라서 
$x_1^{(3)}=g(s_1^{(2)})$이고, $s_1^{(2)}=W_{01}^{(2)}+W_{11}^{(2)}x_1^{(2)}+W_{21}^{(2)}x_2^{(2)}$가 성립함. 이와 동일하게 
$x_2^{(3)}=g(s_2^{(2)})$와   $s_2^{(2)}=W_{02}^{(2)}+W_{12}^{(2)}x_1^{(2)}+W_{22}^{(2)}x_2^{(2)}$ 또한 성립함.
우리는 이 식을 바탕으로 Chain Rule을 적용해 미분해 나갈거임. </p>
<p>손실함수를 $f$ 라고 설정하면, 우리는 각 가중치들을 업데이트하기 위해 각 가중치에 대하여 $f$를 미분한 값을 찾아내야 함.</p>
<p>우선 아래 경로에 대한 미분값을 찾아보겠음.
빨간 경로에 해당하는 가중치 행렬은 $W_{21}^{(2)}$와 $W_{22}^{(2)}$임.
<img src="https://velog.velcdn.com/images/jyoung_710/post/f90165eb-172a-4029-867f-f76f32d6bc25/image.png" alt="">
Chain Rule을 적용하여 나타낸 결과는 아래와 같음.
<img src="https://velog.velcdn.com/images/jyoung_710/post/2f54e132-a179-48e2-8a6f-cb658d8bd7cf/image.png" alt="">
이때 $s$를 $W$에 대해 미분한 값에 대해서는 위의 식을 바탕으로 계산하여 나타낼 수 있음.
또한 그 앞의 두 미분값을 아래와 같이 치환하겠음.
<img src="https://velog.velcdn.com/images/jyoung_710/post/1426e8dd-0f8e-4c45-85aa-1a14b3138a85/image.png" alt="">
다음으로는 아래 빨간색 경로의 가중치에 대한 미분값을 계산해보겠음. 그런데 이번에는 해당 가중치가 뒤의 두 초록색 경로에 영향을 주고 있으므로 이를 고려하여 계산할 필요가 있음.
<img src="https://velog.velcdn.com/images/jyoung_710/post/dec25ae7-ce8d-423b-beb7-db303fef0811/image.png" alt="">
<img src="https://velog.velcdn.com/images/jyoung_710/post/1eb5f4e7-51ff-46eb-98a8-608fc7f0facb/image.png" alt="">
해당 가중치의 값이 마지막 두 노드의 값에 영향을 미치므로 마지막 두 노드를 나누어 미분한 후 더하였음.
위 수식을 자세히 관찰해보면 처음에 델타로 치환하였던 값과 같은 부분도 있고, 직접 미분값을 계산할 수 있는 부분도 있음. 이를 적용하여 식을 풀어내면 아래와 같이 계산됨.
<img src="https://velog.velcdn.com/images/jyoung_710/post/2ef0d5f7-fbf3-4cb1-a380-916925a6b181/image.png" alt="">
수식의 최종 결과를 보면 처음 계산했던 두 가중치 미분 결과와 꼴이 매우 비슷하다는 것을 볼 수 있음. 그리고 델타는 다음 layer의 델타값을 참조하는 것을 보아 <span style="background-color:#D3E3F5; font-weight: bold;">oupput layer에서부터 input layer 방향으로 미분해 나가면 복잡하게 미분할 필요없이 이전에 사용한 미분값을 사용하여 쉽게 계산</span>할 수 있을 것이라는 것도 알 수 있음. </p>
<p>일반화하여 전체 layer의 개수 $L$에 대하여 $l\leq L-2$의 범위를 가질 때 아래와 같은 식이 성립함.
<img src="https://velog.velcdn.com/images/jyoung_710/post/ad672032-0a17-4a65-bc6b-290d406f67fd/image.png" alt=""></p>
<p>정리하면 인공신경망의 학습 과정은 다음과 같음.</p>
<blockquote>
<ol>
<li>순전파 과정을 통해 출력</li>
<li>출력값을 바탕으로 오차함수 계산</li>
<li>오차함수의 각 가중치에 대한 미분계수를 역전파로 계산</li>
<li>얻어낸 미분계수로 경사하강법 적용 후 가중치 업데이트</li>
<li>1~4 과정 반복</li>
</ol>
</blockquote>
<h1 id="span-stylebackground-colore6e6favanishing-gradientspan"><span style="background-color:#E6E6FA">Vanishing Gradient</span></h1>
<p>역전파 계산에 등장하는 문제점 중 하나임. 특히 이 기울기 소실(Vanishing Gradient)은 활성화함수로써 Sigmoid를 사용할 때 많이 발생함. </p>
<p>Sigmoid는 아래와 같이 생겼음.
<img src="https://velog.velcdn.com/images/jyoung_710/post/03c9e7e4-ca09-4fa3-a3ce-e9b25c3a25b1/image.png" alt="">
이 함수는 $x=0$일 때 기울기가 $1/4$으로, 함수 내에서 가장 큰 기울기임. 그리고 $x$가 무한대로 갈수록 그 기울기는 빠르게 0에 가까워짐.</p>
<p>이러한 함수의 특징 때문에 역전파 계산에서 활성화함수를 미분한 값을 사용할 때 layer가 쌓일수록 미분값이 점점 0에 가까워지는 것임. 즉 아무리 layer를 많이 쌓아도 <span style="background-color:#D3E3F5; font-weight: bold;">output layer에 가까운 layer의 가중치만 조금 업데이트되고, input layer에 가까운 layer의 가중치는 거의 업데이트가 안된다</span>는 것임. </p>
<p>따라서 이는 sigmoid에만 해당하는 문제가 아닌 기울기가 점점 0으로 소실되는 함수들 모두에 해당하는 문제가 됨. </p>
<p>그렇다면 어떤 함수를 써야 하는가?
대표적인 함수가 바로 ReLU임.
<img src="https://velog.velcdn.com/images/jyoung_710/post/6bc8b067-1c3b-4cd6-b21c-4e3714fb97d2/image.png" alt="">
sigmoid와 달리 <span style="background-color:#D3E3F5; font-weight: bold;">값이 양수라면 기울기가 항상 1</span>이기 때문에 활성화함수의 미분값으로 인해 기울기가 소실될 문제를 완전히 해결했음. 또한 값이 음수인 값에 대해서는 기울기가 모두 0이기 때문에 모든 노드에 대한 미분값을 반영하는 것이 아닌 일부 노드만 반영한다는 특징도 있음. 이 또한 기울기 소실 문제를 줄이는 데에 영향을 줌.</p>
<p>그렇다면 ReLU를 사용하는 것이 항상 기울기 소실 문제에 대해 옳은가?
그건 또 아님. </p>
<p>ReLU는 값이 하나라도 음수가 나온다면 그 노드에 대한 미분값을 0으로 처리해버리는 상남자 함수이므로, 노드의 개수가 충분하지 않은, 노드의 개수가 매우 적은 신경망에 대해서는 오히려 가중치 업데이트를 정상적으로 하지 못할 가능성이 더 높음. 따라서 <span style="background-color:#D3E3F5; font-weight: bold;">노드의 개수가 충분히 많지 않다면 기존의 sigmoid 함수를 사용하는 것이 기울기 소실이 발생하더라도 더 학습을 잘 할 수 있음.</span></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[MediaPipe Hands: On-device Real-time Hand Tracking]]></title>
            <link>https://velog.io/@jyoung_710/MediaPipe-Hands-On-device-Real-time-Hand-Tracking</link>
            <guid>https://velog.io/@jyoung_710/MediaPipe-Hands-On-device-Real-time-Hand-Tracking</guid>
            <pubDate>Wed, 04 Sep 2024 15:14:33 GMT</pubDate>
            <description><![CDATA[<p><a href="https://arxiv.org/pdf/2006.10214">https://arxiv.org/pdf/2006.10214</a></p>
<h1 id="span-stylebackground-colore6e6fa-abstract-span"><span style="background-color:#E6E6FA"> Abstract </span></h1>
<ul>
<li>우리는 무려 실시간으로 모바일 환경에서도 아주 잘 돌아가는 손 트래킹 방법을 보여줄 것임</li>
<li>파이프라인은 두 갈래로 나뉨<ul>
<li>손바닥 탐지기(Palm Detector) → 손 바운딩 박스 생성</li>
<li>손 특징 모델(Hand Landmark Model) → 손 뼈대 예측</li>
</ul>
</li>
</ul>
<h1 id="span-stylebackground-colore6e6fa-introduction-span"><span style="background-color:#E6E6FA"> Introduction </span></h1>
<h2 id="기존의-문제점">기존의 문제점</h2>
<ul>
<li>기존 연구들의 대부분은 아주 특별한 하드웨어(ex. 깊이 센서)를 사용함</li>
<li>실시간으로 돌릴 수 없는 아주 무거운 모델</li>
<li>따라서 플랫폼에 한계가 발생</li>
</ul>
<h2 id="논문의-차이점-및-기여-내용">논문의 차이점 및 기여 내용</h2>
<ul>
<li>모바일 환경에서도 여러 개의 손을 추적할 수 있음</li>
<li>센서 없이 오직 RGB Input에 대해서도 <u>*2.5D</u>의 손 포즈를 예측함</li>
<li>다양한 플랫폼에서도 사용 가능</li>
</ul>
<blockquote>
<p><span style = 'background-color:#ECE4E2; border-radius: 30%'>📦</span> <strong>2.5D</strong>
일반적으로 말하는 평면의 2D와 깊이(Depth)를 합한 개념
즉 해당 모델은 손의 깊이까지도 센서 없이 예측할 수 있다는 것임,,!</p>
</blockquote>
<h1 id="span-stylebackground-colore6e6fa-architecture-span"><span style="background-color:#E6E6FA"> Architecture </span></h1>
<p>이 모델은 abstract에서 말한 것과 같이 두 개의 모델이 함께 작동함.</p>
<ul>
<li>손바닥 탐지기(Palm Detector)<ul>
<li>바운딩 박스를 통해 손바닥의 위치를 알아냄</li>
<li>입력 이미지는 전체 크기(full)의 이미지</li>
</ul>
</li>
<li>손 특징 모델(Hand Landmark Model)<ul>
<li>높은 정확도의 2.5D 특징점을 return</li>
<li>탐지기로부터 넘어온 잘려진(cropped) 이미지를 입력값으로 받음</li>
</ul>
</li>
</ul>
<p><u>*탐지기에 잘린 이미지를 입력값으로 넣는 이유</u>는 </p>
<ul>
<li>데이터 증강(aumentation)을 줄일 수 있음</li>
<li>특징점(landmark)의 위치를 정확하게 알아낼 수 있도록 함</li>
</ul>
<blockquote>
<p><span style = 'background-color:#ECE4E2; border-radius: 30%'>❓</span> <strong>잘린 이미지와 두 근거 사이의 관계</strong>
데이터 증강은 데이터의 양이 부족하거나 다양한 상황에 대처하기 위해 사용됨
그런데 만약 탐지기의 과정을 거친 잘린 이미지를 특징점 모델의 입력값으로 넣게 되면</p>
</blockquote>
<ul>
<li>복잡한 배경이 제거되며 특징점 모델이 더 안정적으로 학습할 수 있게 됨</li>
<li>탐지기로부터 이미 손바닥의 위치 정보 또한 얻었기 때문에 사진의 위치나 크기를 변형할 필요가 없어짐</li>
</ul>
<p>또한 <u>*이전 프레임에서 특징점 예측으로부터 얻어낸 바운딩 박스를 다음 프레임의 입력으로 사용함</u>으로써 모든 프레임에서 탐지기를 사용할 필요가 없어짐. </p>
<blockquote>
<p><span style = 'background-color:#ECE4E2; border-radius: 30%'>🧐</span> <strong>아까는 탐지기(바운딩 박스) 이후에 특징점 모델이라며,,,?</strong>
아까는 탐지기에서 바운딩 박스를 만든 후 탐지기에서 자른 이미지를 특징점 모델의 입력값으로 사용한다고 했는데,,, 지금은 또 특징점 예측으로 얻어낸 바운딩 박스라고 말하니,,
잘 읽어보니 마지막에</br>
<strong>The detector is only applied on the first frame or when the hand prediction indicates that the hand is lost</strong></br>
라고 적혀있음
이는 즉 탐지기 이후에 특징점 모델이 성립하는 경우는 처음과 손이 감지되지 않았을 때 뿐이라고 이해할 수 있음</br>
이를 바탕으로 깨달은 구조를 직접 그려보았음.
<em>(솔직히 제가 그러놓고 좀 감탄했습니다.)</em>
<img src="https://velog.velcdn.com/images/jyoung_710/post/4963ebb1-fa3c-4e55-a8d4-b11bef96f593/image.png" alt=""></p>
</blockquote>
<ol>
<li>첫 번째 프레임이 들어오면 탐지기가 바운딩 박스를 생성 및 사진을 자름</li>
<li>특징점 모델이 특징점을 예측</li>
<li>예측한 특징점을 바탕으로 바운딩 박스를 새로 업데이트</li>
<li>3번의 바운딩 박스를 두 번째 프레임의 입력값으로 넣음</li>
<li>바운딩 박스를 바탕으로 이미지를 크롭
_5-1. 손을 탐지했다면 특징점 모델의 입력값으로 보내어 2<del>5 과정 반복
_5-2. 손을 탐지하지 못했다면 다시 탐지기로 돌아가 1</del>5 과정 반복</br>
즉 처음에 말한 <strong>탐지기 → 특징점 모델</strong>은 초기 입력된 사진에 대해 이루어지는 과정이고, 이후에 말한 <strong>특징점 → 바운딩 박스</strong>는 그 이후의 과정들을 이야기 한 것이었음</li>
</ol>
<h1 id="span-stylebackground-colore6e6fa-blazepalm-detector-span"><span style="background-color:#E6E6FA"> BlazePalm Detector </span></h1>
<ul>
<li>초기 이미지가 들어올 때 손의 위치를 탐지하는 데에는 모바일 실시간 적용을 위해 최적화한 Single-Shot Detecor를 사용함 (BlazeFace에서 비슷한 방식을 사용)</li>
<li>손은 다양한 크기와 가려지기 쉽다는 등의 이유로 탐지하기 어려움,,, → 우리가 해결해야 할 점</li>
<li>우리는 기존과 다른 방법을 써버림😎</li>
</ul>
<h2 id="손-대신에-손바닥을-탐지하자">손 대신에 손바닥을 탐지하자</h2>
<p>왜냐??</p>
<ul>
<li>손바닥(손가락과 손목 사이의 넓은 평평한 부분)이나 주먹은 일반적인 손(손가락, 손바닥, 손목을 포함)보다 간단하기 때문! → 손가락을 포함하면 관절로 연결된 구조 때문에 탐지가 어려움,,ㅠ</li>
<li><u>*비최대 억제(Non-Maximum Suppression)가 더 잘 작동됨</u></li>
<li><u>*오직 정사각형의 바운딩 박스</u>만을 사용해서 모델링이 가능함</li>
</ul>
<blockquote>
<p><span style = 'background-color:#ECE4E2; border-radius: 30%'>✋</span> <strong>왜 NMS가 손바닥에서 더 잘 작동할까?</strong>
비최대 억제는 객체 탐지에서 한 객체에 대해 Confidence Score가 가장 높은 바운딩 박스만을 남기고, IoU(Intersection over Union)가 높은 박스를 억제함.</br>
손바닥같이 작은 객체들은</p>
</blockquote>
<ul>
<li>바운딩 박스가 겹칠 일이 잘 없고
  → 여러 손 바닥인데 IoU 때문에 인식 안 될 위험 낮아짐</li>
<li>대체로 객체의 경계가 더 분명함
  → 경계가 분명하면 confidence가 높을 확률이 높음</br>
따라서 손바닥에서 NMS가 잘 작동하게 되는 것임</li>
</ul>
<blockquote>
<p><span style = 'background-color:#ECE4E2; border-radius: 30%'>📦</span> <strong>손바닥이 아니라 손도 정사각형 바운딩 박스로 할 수 있지 않나?</strong></p>
</blockquote>
<ul>
<li>손바닥은 대개 균등하게 넙적한 모양을 가지고 있음 
→ 정사각형 박스만으로도 충분함</li>
<li>그러나 일반적으로 손은 손가락 때문에 한쪽으로 길쭉한 모양을 하고 있음
→ 짧은 부분을 기준으로 정사각형 바운딩 박스를 적용하면 포함하지 못하는 부분이 생길 수 있음 
→ 그렇다고 긴 부분을 기준으로 정사각형 바운딩 박스를 적용하면 손을 다 포함하긴 하지만 Bounding 박스라는 이름과 맞지 않게 남는 공간이 많아짐(비효율적)</br>
따라서 일반적으로 손은 손바닥보다 다양한 모양의 바운딩 박스가 필요하게 됨,,</li>
</ul>
<h2 id="encoder-decoder-feature-extractor-사용하자">Encoder-decoder feature extractor 사용하자</h2>
<p><u>*FPN과 같은 *Encoder-decoder feature extractor</u>를 사용하여 작은 객체에 대한 장면 맥락 인식(scene-context awareness)이 가능함</p>
<blockquote>
<p><span style = 'background-color:#ECE4E2; border-radius: 30%'>🔠</span> <strong>Encoder, Decoder, FPN</strong></p>
</blockquote>
<ul>
<li>Encoder: 이미지에서 고차원의 특징을 추출한 채로 해상도를 낮추는 것</li>
<li>Decoder: 이미지의 중요한 정보를 유지한 채 해상도를 높여 복원하는 것</li>
<li>FPN(Feature Pyramid Networks): Encoding 으로 이미지의 중요한 특징을 추출해내고, 이 특징을 살린 채로 다양한 해상도로 Decoding함 . 이후 여러 해당도의 피처맵들을 하나로 결합함
→ 다양한 크기의 객체에 대응이 가능해짐</br>
  아래는 해당 논문에서 제시한 FPN구조임
<img src="https://velog.velcdn.com/images/jyoung_710/post/b0118a18-a708-42d3-b3bb-ed734760dac8/image.png" alt="">
위 사진을 보면 차근차근 인코딩하다가 8x8 크기에서 다시 디코딩하는 것을 볼 수 있음.
기본적으로 각 해상도에서 2개의 앵커를 뽑아내지만, 8x8 크기의 해상도에서만 앵커가 6개임. 이는 해당 모델에서 작은 크기의 객체를 더 잘 탐지하기 위해 8x8의 앵커의 개수를 늘렸다고 이해할 수 있음.</li>
</ul>
<h2 id="focal-loss를-최소화하자">Focal Loss를 최소화하자</h2>
<p>높은 스케일 분산(High Scale Variance)을 갖는 많은 앵커들을 더 잘 학습하기 위해 <u>*Focal Loss</u>를 최소화함.</p>
<blockquote>
<p><span style = 'background-color:#ECE4E2; border-radius: 30%'>📷</span> <strong>Focal Loss &amp; High Scale Variance</strong>
Focal Loss는 간단히 말해 배경과 같이 탐지하기 쉬운 부분에 대한 가중치를 낮추고, 객체와 같이 탐지하기 어려운 부분에 대한 가중치를 더 부여하는 손실함수 중 하나임.</br>
우리가 탐지하고자 하는 손바닥은 전체 사진에서 차지하는 부분이 매우 작은 객체이므로 해당 손실함수를 사용하는 것이 더 효율적일 것.</br>
여기서 말하는 High Scale Variance는 FPN 구조를 통해 다양한 해상도에서 앵커를 추출하며 발생한 것임. 당연히 다양한 해상도에서 앵커를 추출했으므로 앵커의 크키가 아주 작지만 다양하고, 또 매우 많을 거임.<br>→ 여기서 나온 작은 앵커들을 Focal Loss로 학습한다고 이해하면 됨
<img src="https://velog.velcdn.com/images/jyoung_710/post/f5fbf225-71fa-4f40-9a31-3e05084b0f73/image.png" alt="">
결과를 보았을 때, Decoder와 Focal Loss를 함께 사용하는 것이 더 정확도가 높은 것을 알 수 있음.</p>
</blockquote>
<h1 id="span-stylebackground-colore6e6fa-hand-landmark-model-span"><span style="background-color:#E6E6FA"> Hand Landmark Model </span></h1>
<p>탐지기 이후의 과정인 특징점 모델은 21개의 2.5D 특징점들의 좌표를 Regression을 통해 탐지함</p>
<p>이 모델의 출력값은 세 개로 나누어짐</p>
<ul>
<li><p>21개의 특징점들의 2.5D 좌표</p>
</li>
<li><p>손이 존재할 확률
  → 이 확률이 임계값보다 낮으면 탐지기가 호출되는 것!</p>
</li>
<li><p>손잡이에 대한 이진 분류</p>
</li>
</ul>
<p>특징점 모델의 세 출력과 입력되는 데이터들의 관계는 아래와 같음.
<img src="https://velog.velcdn.com/images/jyoung_710/post/c425bf36-f930-4522-a496-a8c419865527/image.png" alt=""></p>
<p>여기서 특이한 점은 <u>손목 좌표를 학습할 때에는 오직 인위적인 이미지(Synthetic Image)만</u>을 사용한다는 것임.</p>
<blockquote>
<p><span style = 'background-color:#ECE4E2; border-radius: 30%'>🧐</span> <strong>손목 좌표 학습에는 왜 인위적인 이미지만 사용할까?</strong>
우선 해당 논문에서 사용하는 이미지 데이터셋의 종류는 크게 두 가지임.</p>
</blockquote>
<ul>
<li>현실 이미지(Real World Image)
→ 실내, 야외에서 직접 찍은 사진</li>
<li>인위적 이미지(Synthetic Image)
→ 컴퓨터 그래픽을 활용해 인위적으로 만든 사진</li>
</ul>
<p></br>손목은 특히 다른 부위에 비해 작은 신체 부위이므로, 정밀한 라벨링이 필요함. 그러나 현실 이미지에서 손목을 학습시킬 경우, 주변 환경 요인의 영향으로 제대로 학습되지 않을 가능성이 있음. 
컴퓨터 그래픽을 활용해 이미지를 만든다면 원하는 부위를 원하는 상황에 맞추어 이미지를 얻어낼 수 있기에 현실 이미지보다 더 높은 학습 정확도를 만들어낼 수 있을 것임. 
모든 이미지를 인위적으로 만들어내기에는 현실과 다른 결과를 출력할 수도 있기에, 작은 부위인 손목에만 인위적 이미지만을 사용하여 학습시키는 것으로 예상됨. </p>
<h1 id="span-stylebackground-colore6e6fa-dataset-and-annotation-span"><span style="background-color:#E6E6FA"> Dataset and Annotation </span></h1>
<p><img src="https://velog.velcdn.com/images/jyoung_710/post/f6475926-5db2-4cb8-8b3a-c4a9bd5a93f2/image.png" alt=""></p>
<h2 id="palm-detector">Palm Detector</h2>
<p>손바닥을 탐지하는 데에는 현실 이미지만을 사용함.
현실 이미지만으로도 손바닥의 위치를 다양하게 학습하기에 충분하기 때문.</p>
<h2 id="hand-landmark-model">Hand Landmark Model</h2>
<p>특징점 모델에는 현실 이미지와 인위적 이미지를 모두 사용함.
현실 이미지에 21개의 특징점에 대한 주석을 모두 달고, 인위적 이미지는 3D 관절을 투영하여 정답 데이터(Ground-Truth)로 사용함.</p>
<h2 id="hand-presence">Hand Presence</h2>
<p>손의 존재성에 대해 학습할 때에는 일부 현실 이미지를 사용함. 
Positive Example에는 현실 이미지가 그대로 사용되며, Negative Example에는 주석처리된 부분을 제외한 부분을 사용함.</p>
<h2 id="handedness">Handedness</h2>
<p>손잡이를 학습할 때에는 일부 현실 이미지에 주석을 달아 학습함.</p>
<h1 id="span-stylebackground-colore6e6fa-results-span"><span style="background-color:#E6E6FA"> Results </span></h1>
<p><img src="https://velog.velcdn.com/images/jyoung_710/post/a01c3293-a1f3-4084-8290-1cd1e61a0007/image.png" alt="">
위 표를 통해서 현실 이미지 혹은 인위적 이미지 중 하나만을 사용하였을 때보다 둘을 함께 사용했을 때 더 나은 성능을 보임을 알 수 있음. 또한 인위적 이미지만을 사용했을 때에 MSE가 25.7%로 높지만 현실 이미지와 함께할 때 13.4%로 확연하게 낮아지는 것으로 보았을 때, 현실 이미지의 비율을 더 확장하면 모델의 일반화 능력을 더 향상시킬 수 있을 것이라 기대할 수 있음.</p>
</br>

<p><img src="https://velog.velcdn.com/images/jyoung_710/post/08542525-4918-4ad8-b901-8bbdd32a83fa/image.png" alt="">
위 표에서는 파라미터의 정도에 따라 모델의 크기를 나누고, 모바일 별 실행 시간 비교하고 있음. Heavy는 확실히 파라미터의 양이 많아진 만큼 오차가 줄었지만 실행 시간이 너무 오래 걸리는 것을 볼 수 있고, Light와 Full은 Heavy에 비해 빠르지만 오차가 조금 더 큰 것을 볼 수 있음.
해당 논문에서는 Full 모델이 우수한 질과 속도를 가지고 있다고 소개하고 있음.
(세 모델 중 속도와 오차를 고려할 때, 가장 적당한 모델으로 생각됨)</p>
<h1 id="span-stylebackground-colore6e6fa-implementation-in-mediapipe-span"><span style="background-color:#E6E6FA"> Implementation in MediaPipe </span></h1>
<p><img src="https://velog.velcdn.com/images/jyoung_710/post/c1a37353-487d-4947-afb2-c913ec01d7cb/image.png" alt="">
해당 파트에서는 위 그래프에 그려진 대로 작동되며, 이 모든 것이 구글의 MediaPipe로 이루어짐을 이야기함. 
앞서 이야기했던 내용을 그대로 요약하여 나타내었기에 추가적으로 더 설명하지는 않겠음.</p>
<h1 id="span-stylebackground-colore6e6fa-application-examples-span"><span style="background-color:#E6E6FA"> Application examples </span></h1>
<p>해당 논문에서는 활용 예시로 크게 두 가지를 제안하고 있음.</p>
<h2 id="gesture-recognition">Gesture Recognition</h2>
<h3 id="static-gesture-recognition">Static Gesture Recognition</h3>
<p>아래의 알고리즘을 통해 정적인 제스처를 인식함. </p>
<ol>
<li>손가락의 상태(굽은 상태, 펴진 상태)를 관절의 누적된 각도를 통해 결정함</li>
<li>손가락 상태의 집합(누적된 각도)을 미리 정의한 제스처 집합에 매핑
<img src="https://velog.velcdn.com/images/jyoung_710/post/da5e05f8-4e5b-4eb6-bb46-0c1a6c861000/image.png" alt="">
예를 들어 모든 손가락을 다 펴게 되면 손가락을 다 폈을 때의 각도들의 집합이 FIVE 제스처에 해당하는 집합과 매핑되고, FIVE라고 인식되는 것.<h3 id="dynamic-gesture-recognition">Dynamic Gesture Recognition</h3>
정적 제스처 인식에서 나아가 랜드마크의 시퀀스를 사용하여 동적 제스처도 인식할 수 있음.<h2 id="ar">AR</h2>
논문에서는 손의 골격을 바탕으로 하여 골격 위에 AR 효과를 씌우는 것을 제안함. 아래 그림과 같이 골격을 네온 스타일로 랜더링 할 수 있음.
<img src="https://velog.velcdn.com/images/jyoung_710/post/3b2241f6-6b7b-43af-ba01-3b80e2dd3d69/image.png" alt=""><h1 id="span-stylebackground-colore6e6fa-conclusion-span"><span style="background-color:#E6E6FA"> Conclusion </span></h1>
</li>
</ol>
<ul>
<li>MediaPipe Hands라는 <u>*End-to-End</u> 손 추적 방안을 제시함.</li>
<li>이것을 오픈 소스로 공개하여 제스처 컨트롤 및 AR/VR 활용에 우리의 파이프라인을 사용할 수 있도록 함.<blockquote>
<p><span style = 'background-color:#ECE4E2; border-radius: 30%'>🧐</span> <strong>End-to-End  모델</strong>
입력부터 최종 결과까지 전체 과정을 하나의 시스템으로 처리할 수 있는 모델
<img src="https://velog.velcdn.com/images/jyoung_710/post/05ecff27-6002-48c4-aa61-d4967ef226af/image.png" alt=""></p>
</blockquote>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[FaceNet, Picasa, MTCNN 논문 읽어보기]]></title>
            <link>https://velog.io/@jyoung_710/FaceNet-Picasa-MTCNN-%EB%85%BC%EB%AC%B8-%EC%9D%BD%EC%96%B4%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@jyoung_710/FaceNet-Picasa-MTCNN-%EB%85%BC%EB%AC%B8-%EC%9D%BD%EC%96%B4%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Sun, 25 Aug 2024 20:05:26 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><span style = 'background-color:#ECE4E2; border-radius: 30%'>❗</span> 얼굴 인식 프로젝트 진행 중 사용한 FaceNet의 깊은 이해를 위해 처음으로 논문을 제대로 읽어본 터라,, 잘못된 개념 및 부족한 사항이 존재할 수 있습니당,,,ㅎ 
<span style="background-color:#D3E3F5; font-weight: bold;">피드백은 언제나 환영입니다😄</span></p>
</blockquote>
<h1 id="span-stylebackground-colore6e6fa-facenet-span"><span style="background-color:#E6E6FA"> FaceNet </span></h1>
<p><a href="https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Schroff_FaceNet_A_Unified_2015_CVPR_paper.pdf">https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Schroff_FaceNet_A_Unified_2015_CVPR_paper.pdf</a></p>
<h2 id="facenet의-기본-원리">FaceNet의 기본 원리</h2>
<p>FaceNet은 얼굴 인식(recognition), 검증(verification), 군집화(clustering)과 같은 작업을 위해 설계된 <span style="background-color:#D3E3F5; font-weight: bold;">CNN 기반의 딥러닝 모델</span>이다. 이 모델의 핵심 아이디어는 얼굴 이미지를 고정된 차원의 벡터 공간으로 변형(임베딩, embedding)하는 것이다. 두 얼굴의 이미지가 같은 사람인지의 여부를 <span style="background-color:#D3E3F5; font-weight: bold;">임베딩 공간에서의 거리</span>를 통해 판단한다. 
<img src="https://velog.velcdn.com/images/jyoung_710/post/09f48a27-5e9d-412e-9fec-3516e2558267/image.png" alt=""></p>
<h2 id="임베딩-공간">임베딩 공간</h2>
<p>임베딩 공간은 고차원의 유클리드 공간으로, FaceNet은 각 얼굴 이미지를 이 공간의 한 점으로 매핑(mapping)한다.</p>
<blockquote>
<p><span style = 'background-color:#ECE4E2; border-radius: 30%'>🔨</span> <strong>임베딩(Embedding) in Image</strong>
이미지에서의 임베딩은 <span style="background-color:#D3E3F5; font-weight: bold;">고차원의 이미지 데이터를 저차원 벡터 공간</span>으로 변환하는 방법을 말한다. 
RGB 이미지는 MxNx3의 크기를 가지는데, 만약 1,280×720×3 크기의 사진이라면 크기가 2,764,800 차원으로 매우 커지게 되는 것이다. 과연 이 모든 차원이 의미가 있을지 생각해본다면, 그렇지 않으므로 임베딩 과정에서 데이터의 중요한 특징을 유지하면서도 표현을 단순화하거나 효율적으로 만드는 것을 목표로 한다.</br>
임베딩의 주요한 목적을 정리하면 아래와 같다.</p>
</blockquote>
<ul>
<li>불필요한 노이즈나 중복 정보를 제거</li>
<li>임베딩 공간에서 벡터 간의 거리를 사용해 유사도 측정 가능</li>
<li>메모리 사용량과 계산 복잡도가 줄어들게 됨</li>
</ul>
<p>FaceNet에서는 고차원의 이미지 데이터를 128차원의 벡터로 임베딩했다.(여전히 높은 차원이지만 원본 이미지 차원에 비하면 &quot;비교적&quot; 매우 작은 차원이다.) 각 차원은 얼굴의 특정한 특징을 나타내게 되며, 임베딩의 과정은 다음과 같다.</p>
<ol>
<li>이미지 입력 : 고차원의 픽셀 데이터로 표현된 원본 얼굴의 이미지를 입력한다.</li>
<li>특징 추출 : 합성곱 신경망(CNN)이 얼굴 이미지에서 특징을 추출한다. 이 과정을 통해 <span style="background-color:#D3E3F5; font-weight: bold;">얼굴의 중요한 시각적 특징</span>들이 강조된다.</li>
<li>임베딩 벡터 생성: 추출된 특징을 바탕으로 <span style="background-color:#D3E3F5; font-weight: bold;">이미지 데이터를 벡터</span>로 표현하고, FaceNet의 학습을 통해 <span style="background-color:#D3E3F5; font-weight: bold;">특정 임베딩 공간</span>으로 변환한다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/jyoung_710/post/4b375c35-5112-4f33-a86d-c3a8cc4be49a/image.png" alt=""></p>
<h2 id="triplet-손실함수">Triplet 손실함수</h2>
<p>FaceNet은 Triplet 손실함수를 사용한다. 이 손실함수는 세 가지 얼굴 이미지를 사용하여 모델을 훈련시킨다. 
세 가지 얼굴 이미지 종류는 다음과 같다.</p>
<ul>
<li>🔵Anchor : 기준이 되는 얼굴 이미지</li>
<li>💚Positive : Anchor와 같은 사람의 다른 얼굴 이미지</li>
<li>🔴Negative : Anchor와 다른 사람의 얼굴 이미지</li>
</ul>
<p>Triplet 손실함수의 목표는 <span style="background-color:#D3E3F5; font-weight: bold;">Anchor와 Positive의 거리는 가깝게, Anchor와 Negative의 거리는 멀게</span> 만드는 것이다. 수식으로 표현하면 아래와 같다.</p>
<p>$|x_a^i - x_p^i|_2^2 + \alpha &lt; |x_a^i - x_n^i|_2^2, \quad \forall (x_a^i, x_p^i, x_n^i) \in T$</p>
<p>이때 $a$는 Margin으로, 거리 차이의 최솟값을 의미한다.</p>
<p>FaceNet에서 사용한 손실함수의 수식은 다음과 같다.</p>
<p>$L = \sum_{i=1}^{N} \left[ |f(x_a^i) - f(x_p^i)|<em>2^2 - |f(x_a^i) - f(x_n^i)|_2^2 + \alpha \right]</em>+$</p>
<p>수식을 바탕으로 손실함수를 해석해보면 시그마 안의 수식이 <span style="background-color:#D3E3F5; font-weight: bold;">Anchor-Positive 와 Anchor-Negative 의 벡터 간의 거리 제곱 차가 최소한 $a$(Margin) 이상</span>이 되어야 0이 출력된다. 이는 단순히 Positive가 Negative보다 Anchor에 가깝기만 하면 되는 것이 아니라 $a$ 만큼 더 가까워야 손실함수가 최소가 된다는 것이다.
이를 바탕으로 학습한 결과는 아래와 같다.
<img src="https://velog.velcdn.com/images/jyoung_710/post/913f91dd-b0b5-43a3-8262-34ac4c722e08/image.png" alt=""></p>
<h1 id="span-stylebackground-colore6e6fapicasaspan"><span style="background-color:#E6E6FA">Picasa</span></h1>
<p><a href="https://link.springer.com/chapter/10.1007/978-3-319-10599-4_8">https://link.springer.com/chapter/10.1007/978-3-319-10599-4_8</a>
Picasa는 FaceNet 논문에서 사용했던 얼굴 탐지(Detection) 모델이다. 
이 논문에서는 <span style="background-color:#D3E3F5; font-weight: bold;">얼굴 정렬(face alignment)</span>을 이용하여 얼굴 탐지의 성능을 향상시켰다. 얼굴 정렬을 통해 얻어진 정렬된 얼굴이 더 나은 feature를 제공하고, 얼굴과 비얼굴을 구별하는 정확도가 높아진다.</p>
<h2 id="cascade-face-detection">Cascade Face Detection</h2>
<p>Cascade face detection은 얼굴 검출 알고리즘 중 하나로 이미지에서 얼굴을 효율적으로 탐지하기 위해 일련의 단계를 통해 점진적으로 얼굴과 비얼굴을 구분하는 방법이다. 
단계적으로 이미지의 각 영역에 대해 <span style="background-color:#D3E3F5; font-weight: bold;">약한 분류기(weak classifier)</span>를 적용하여 얼굴이 아닌 영역을 빠르게 배제하고, <span style="background-color:#D3E3F5; font-weight: bold;">얼굴일 가능성이 높은 영역에 대해 더 정밀한 검사</span>를 진행한다. 
_<span style = "color:gray">(마치 영단어를 외울 때 아는 것은 빠르게 넘기고 모르는 것만 집중적으로 외우는 것과 같다고 볼 수 있다.)</span>_</p>
<p><img src="https://velog.velcdn.com/images/jyoung_710/post/07fe1f25-9c44-40e3-a24e-5fc2782eda67/image.png" alt=""></p>
<blockquote>
<p><span style = 'background-color:#ECE4E2; border-radius: 30%'>🧐</span> <strong>특징 추출</strong>
일반적으로 사용되는 특징 추출 방법은 Haar-like features로, 이는 <span style="background-color:#D3E3F5; font-weight: bold;">이미지의 밝기 차이</span>를 기반으로 얼굴의 특정 패턴(눈, 코, 입,,)을 포착할 수 있는 간단한 특징이다. 특정 영역 내에서 픽셀 강도의 합을 계산하고 이를 인접 영역과 비교하는 방식으로 계산한다. </br>예를 들어, 눈은 얼굴의 상단 부분에서 밝기가 낮은 영역으로 나타나고, 위에 있는 밝은 영역인 이마와 비교하여 특정 패턴을 형성한다.</p>
</blockquote>
<h2 id="face-alignment">Face Alignment</h2>
<p>사진에서 얼굴의 위치와 포즈가 다양할 수 있기 때문에 정렬되지 않은 사진에 대해서는 얼굴 탐지이 정확도가 떨어질 수 있다. 이때 정렬(alignment)은 얼굴 이미지 내에서 눈, 코, 입 등의 <span style="background-color:#D3E3F5; font-weight: bold;">주요 특징점(facial landmarks)</span>을 찾아내고, 이를 활용해 <span style="background-color:#D3E3F5; font-weight: bold;">얼굴을 표준적인 위치</span>로 맞추는 작업이다.</p>
<h2 id="joint-cascade-structure">Joint Cascade Structure</h2>
<p>face alignment와 face detection을 동일한 cascade structure 내에서 동시에 학습하는 방법이다.</p>
<h3 id="1-간단한-검출-및-초기값-설정">1. 간단한 검출 및 초기값 설정</h3>
<p>이미지 내의 각 <span style="background-color:#D3E3F5; font-weight: bold;">검출 후보 영역에 대해 mean face shape</span>을 초기값으로 설정한다.
이는 후보 영역에 평균적인 얼굴 모양을 대입하여 임의로 설정한다는 뜻이다.
<img src="https://velog.velcdn.com/images/jyoung_710/post/cbffcd46-9814-4d4a-b7da-68fdb5f1c2f1/image.png" alt=""></p>
<h3 id="2-정렬과-검출의-반복">2. 정렬과 검출의 반복</h3>
<p>검출된 후보 영역에서 얼굴의 <span style="background-color:#D3E3F5; font-weight: bold;">특징점을 점진적으로 조정</span>하면서 이를 기반으로 <span style="background-color:#D3E3F5; font-weight: bold;">추가적인 특징을 추출</span>한다. 각 단계에서 분류기와 회귀기는 현재가지 조정된 얼굴 모양을 기반으로 학습된다. 해당 과정을 각 후보영역에 대해 반복한다. 
단계를 지날수록 <span style="background-color:#D3E3F5; font-weight: bold;">더 복잡한 분류기와 회귀기</span>를 사용하여 검출과 정렬의 정확도를 높인다. 각 단계에서 후보의 개수는 줄어들고 남은 후보들의 정렬 정확도는 향상된다. </p>
<h3 id="3-최종-결과">3. 최종 결과</h3>
<p>모든 단계를 통과한 후보 영역이 얼굴로 최종 검출되며, 해당 영역에 대한 얼굴 모양도 함게 검출된다.
<img src="https://velog.velcdn.com/images/jyoung_710/post/be491083-c817-495f-9725-b8802d9cb482/image.png" alt=""></p>
<h1 id="span-stylebackground-colore6e6famtcnnspan"><span style="background-color:#E6E6FA">MTCNN</span></h1>
<p><a href="https://arxiv.org/abs/1604.02878">https://arxiv.org/abs/1604.02878</a>
해당 모델은 현재 진행하고 있는 FaceNet을 바탕으로 한 얼굴 인식 프로젝트의 레퍼런스에서 사용한 것이기에 조사하여 읽어보았다.
(레퍼런스: <a href="https://github.com/ColdBottle0226/FaceNet">https://github.com/ColdBottle0226/FaceNet</a>)</p>
<p>이 논문에서는 <span style="background-color:#D3E3F5; font-weight: bold;">다중 작업 학습</span>을 통해 통합된 Cascade CNN을 제안한다. 이 제안된 CNN은 세 단계로 구성된다. 첫 번째 단계에서는 간단한 CNN을 사용하여 <span style="background-color:#D3E3F5; font-weight: bold;">후보 창을 신속하게 생성</span>하고, 두 번째 단계에서는 더 복잡한 CNN으로 <span style="background-color:#D3E3F5; font-weight: bold;">비얼굴 후보를 제거</span>한다. 마지막 단계에서는 가장 복잡한 CNN을 사용하여 <span style="background-color:#D3E3F5; font-weight: bold;">얼굴 랜드마크 위치</span>를 출력한다. </p>
<p>또한 online hard sample mining으로 학습 과정에서 <span style="background-color:#D3E3F5; font-weight: bold;">자동으로 어려운 샘플을 선택하여 성능을 향상</span>시키는 새로운 전략을 도입했다. 이는 detection model을 강화하는 데에 덜 도움이 되는 쉬운 샘플을 무시하여 수동 샘플 없이도 더 나은 성능을 보여준다.</p>
<h2 id="전반적인-과정">전반적인 과정</h2>
<p><img src="https://velog.velcdn.com/images/jyoung_710/post/9926bf93-ac35-4496-ae14-9adb96a7637f/image.png" alt=""></p>
<h3 id="1단계">1단계</h3>
<p>우선 이미지가 주어지면 이를 다양한 스케일로 resize하여 <span style="background-color:#D3E3F5; font-weight: bold;">이미지 피라미드</span>를 만든다. 이 피라미드는 다음 세 단계로 이루어진 Cascade 프레임워크의 input이 된다.</p>
<blockquote>
<p><span style = 'background-color:#ECE4E2; border-radius: 30%'>🧐</span> <strong>이미지 피라미드를 왜 만드는가?</strong></p>
</blockquote>
<ul>
<li>이미지를 다양한 크기로 조절함으로써 단일 크기의 얼굴만 탐지하는 것이 아니라, 이미지 내의 모든 크기의 얼굴을 탐지할 수 있다.</li>
<li>detection 모델의 구조나 가중치를 변경하지 않아도 된다.</li>
<li>여러 스케일의 얼굴을 학습하면 비스듬히 보이는 얼굴도 효과적으로 탐지할 수 있다.</li>
</ul>
<h3 id="2단계">2단계</h3>
<p><span style="background-color:#D3E3F5; font-weight: bold;">Proposal Network(P-Net)</span>을 사용하여 <span style="background-color:#D3E3F5; font-weight: bold;">후보 창과 그들의 Bounding Box 회귀 벡터를 추출</span>한다. 이후 추정된 바운딩 박스 회귀 벡터를 사용하여 후보 창을 보정하고, 비최대 억제(NMS)를 적용하여 중복된 후보 창을 병합한다.</p>
<blockquote>
<p><span style = 'background-color:#ECE4E2; border-radius: 30%'>🧐</span> <strong>비최대 억제(Non-Maximun Suppression, NMS)</strong>
NMS는 Bounding Box에 대한 Confidence Score가 가장 높은 Box를 선택하고, 해당 Box와의 IoU가 threshold보다 높은 Box를 제거하는 방법이다. </p>
<blockquote>
<p><strong>IoU(Intersection over Union)</strong>
말 그대로 교집합/합집합 을 말한다. 
두 개 이상의 Bounding Box가 있을 때 Box가 많이 겹쳐질수록 IoU가 높아진다. </p>
</blockquote>
</blockquote>
<h3 id="3단계">3단계</h3>
<p>모든 후보 창은 또 다른 CNN인 <span style="background-color:#D3E3F5; font-weight: bold;">Refinement Network(R-Net)</span>에 입력되며, 이 네트워크는 더 많은 비얼굴 후보를 제거하고, Bounding Box 회귀로 보정하며, NMS로 후보 창을 병합한다.</p>
<h3 id="4단계">4단계</h3>
<p>마지막은 Output Network(O-Net)을 거치며, 두 번째 단계와 유사하지만 이 네트워크는 <span style="background-color:#D3E3F5; font-weight: bold;">다섯 개의 얼굴 특징점(landmark) 위치</span>를 출력한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Activation Function]]></title>
            <link>https://velog.io/@jyoung_710/Activation-Function</link>
            <guid>https://velog.io/@jyoung_710/Activation-Function</guid>
            <pubDate>Thu, 22 Aug 2024 02:26:25 GMT</pubDate>
            <description><![CDATA[<h1 id="span-stylebackground-colore6e6fa-activation-functionspan"><span style="background-color:#E6E6FA"> Activation Function?</span></h1>
<p>🔥<span style="background-color:#D3E3F5; font-weight: bold;">활성화 함수(Activation Function)</span>는 심층 신경망(Deep Neural Network, DNN)에서 은닉층의 레이어를 활성화해주기 위해 사용되는 함수이다.
<img src="https://velog.velcdn.com/images/jyoung_710/post/f4d1ce8c-579b-4082-9732-22bb3084ee8d/image.png" alt="">
위와 같은 MLP(Multi Layer Perceptron)가 있다고 할 때, Hidden Layer 1에서 Hidden Layer 2로 전달되기 전에 각 노드에서 활성화 함수를 거쳐 계산한 후 다음 레이어로 전달한다.
퍼셉트론에서 활성화 함수를 계산하는 과정은 다음과 같다.
<img src="https://velog.velcdn.com/images/jyoung_710/post/a5388592-be89-4d93-b66c-50cd39dc5310/image.png" alt="">  </p>
<p>이러한 활성화 함수에는 ReLU, Sigmoid, tanh 등 다양하게 존재하고, 이들의 공통점은 선형적이지 않은 비선형적 함수라는 것이다. </p>
<h1 id="span-stylebackground-colore6e6fawhy-non-linearspan"><span style="background-color:#E6E6FA">Why Non-Linear?</span></h1>
<blockquote>
<p><span style = 'background-color:#ECE4E2; border-radius: 30%'>🧐</span> 왜 활성화 함수가 선형 함수이면 안되는 걸까?</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/jyoung_710/post/fdea0455-976f-4f78-af5d-5bed105bb55f/image.png" alt="">
위와 같은 인공신경망이 있다고 하자.
그렇다면 input 값은 $\overrightarrow{x} = \begin{bmatrix}x_1\x_2\ \end{bmatrix}$, 첫 번째 가중치 행렬은 $W_1 = \begin{bmatrix}w_1&amp;w_2\w_3&amp;w_4\w_5&amp;w_6 \end{bmatrix}$, 첫 번째 편향은 $\overrightarrow{b_1} = \begin{bmatrix}b_1\b_2\b_3 \end{bmatrix}$ 와 같이 나타낼 수 있다.</p>
<p>이를 바탕으로 첫 번째 Hidden layer의 값은 아래와 같다.</p>
<p>$W_1\overrightarrow{x}+\overrightarrow{b_1} = \begin{bmatrix}w_1&amp;w_2\w_3&amp;w_4\w_5&amp;w_6 \end{bmatrix}\begin{bmatrix}x_1\x_2\ \end{bmatrix}+\begin{bmatrix}b_1\b_2\b_3\end{bmatrix}=\begin{bmatrix}x _{1} w _{1} +x _{2} w _{2} +b _{1} \x _{1} w _{3} +x _{2} w _{4} +b _{2} \x _{1} w _{5} +x _{2} w _{6} +b _{3}\end{bmatrix}$</p>
<p>첫 번째 Hidden Layer에서 다음 Layer로 넘어가기 전 활성화 함수 $f_1$을 거치게 되며, 따라서 다음 Layer에 전달되는 값은 아래와 같다.</p>
<p>$f_1(\begin{bmatrix}x _{1} w _{1} +x _{2} w _{2} +b _{1} \x _{1} w _{3} +x _{2} w _{4} +b _{2} \x _{1} w _{5} +x _{2} w _{6} +b _{3}\end{bmatrix})$</p>
<p>지금의 방식과 똑같이 적용하면 두 번째 Layer에서 다음 Layer로 전달되는 값은 
$f_2(W_2f_1(\begin{bmatrix}x _{1} w _{1} +x _{2} w _{2} +b _{1} \x _{1} w _{3} +x _{2} w _{4} +b _{2} \x _{1} w _{5} +x _{2} w _{6} +b _{3}\end{bmatrix})+\overrightarrow{b_2})$ 일 것이다.</p>
<p>그런데 이때 만약 활성화 함수 $f_1$과 $f_2$가 $y=x$ 꼴의 선형 함수라고 해보자.</p>
<p>그러면 위의 식은 </p>
<p>$W_2\begin{bmatrix}x _{1} w _{1} +x _{2} w _{2} +b _{1} \x _{1} w _{3} +x _{2} w _{4} +b _{2} \x _{1} w _{5} +x _{2} w _{6} +b _{3}\end{bmatrix}+\overrightarrow{b_2} = W_2(W_1\overrightarrow{x}+\overrightarrow{b_1})+\overrightarrow{b_2}=W_2W_1\overrightarrow{x} + W_2\overrightarrow{b_1}+\overrightarrow{b_2}$ 이고</p>
<p>$W_2W_1$을 2x2 크기의 행렬 $W$로, $W_2\overrightarrow{b_1}+\overrightarrow{b_2}$를 2x1 크기의 벡터 $\overrightarrow{b}$로 치환하여 표현하면</p>
<p>$W\overrightarrow{x}+\overrightarrow{b}$이다.</p>
<p>해당 식은 마치 $W$라는 가중치 행렬과 $\overrightarrow{b}$라는 편향을 바탕으로 계산된 식과 같다. 우리는 여러 Layer를 쌓기를 원했으나 <span style="background-color:#D3E3F5; font-weight: bold;">실질적으로는 하나의 Layer를 거친 것</span>과 다르지 않다는 것이고, 이는 우리가 MLP에서 Multi-Layer를 설계한 근본적인 이유가 반영되지 않았다는 것이다. 꼭 $y=x$가 아니더라도 $y=ax+b$의 일반적인 선형 함수가 활성화 함수로 사용되어도 결과는 같다. </p>
<p>따라서 MLP에서 Multi_Layer를 살리기 위해서는 <span style="background-color:#D3E3F5; font-weight: bold;">활성화 함수가 비선형적 함수</span>이어야 한다.</p>
<h1 id="span-stylebackground-colore6e6fasigmoidspan"><span style="background-color:#E6E6FA">Sigmoid</span></h1>
<p><img src="https://velog.velcdn.com/images/jyoung_710/post/2b8ced04-1ffa-4336-a8e8-dfd118f3b0de/image.png" alt="">
시그모이드 함수의 식은 로지스틱 회귀에 사용되는 식과 같다.</p>
<blockquote>
<p>🙆‍♂️ <span style="background-color:#D3E3F5; font-weight: bold;"> 장점 </span></p>
</blockquote>
<ul>
<li>Step Function에 비해 정보 손실이 적다</li>
<li>어떤 입력 값에도 항상 0과 1사이의 연속적인 값을 반환하여 확률로 해석이 가능하다</li>
<li>전 구간에서 미분이 가능하므로 역전파 계산이 가능하다</li>
<li>바이너리 분류 문제에 사용 가능하다</li>
</ul>
<blockquote>
<p>🙅‍♂️ <span style="background-color:#ff8a89; font-weight: bold;"> 단점  </span></p>
</blockquote>
<ul>
<li>기울기 소실 문제가 발생할 수 있다</li>
<li>함숫값의 중심이 0이 아니기에 학습이 잘 안될 수도 있다 (zigzag 문제)</li>
<li>exp 연산으로 인해 학습 속도가 느려질 수 있다</li>
</ul>
<h1 id="span-stylebackground-colore6e6farelurectified-linear-unitspan"><span style="background-color:#E6E6FA">ReLU(Rectified Linear Unit)</span></h1>
<p><img src="https://velog.velcdn.com/images/jyoung_710/post/9c6c5565-b41c-488f-ba13-ee1a9d332ece/image.png" alt=""></p>
<p>현재 딥러닝에서 가장 널리 사용되는 활성화 함수 중 하나이다.</p>
<blockquote>
<p>🙆‍♂️ <span style="background-color:#D3E3F5; font-weight: bold;"> 장점 </span></p>
</blockquote>
<ul>
<li>기울기 소실 문제가 완화된다</li>
<li>학습 속도가 매우 빠르다</li>
<li>양수에서 포화(saturation)가 발생하지 않는다</li>
</ul>
<blockquote>
<p>🙅‍♂️ <span style="background-color:#ff8a89; font-weight: bold;"> 단점  </span></p>
</blockquote>
<ul>
<li>함숫값의 중심이 0이 아니기에 학습이 잘 안될 수도 있다 (zigzag 문제)</li>
<li>일부 뉴런에서 0만 출력하는 문제가 발생하기도 한다</li>
<li>$x$가 0일 때 미분이 되지 않는다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Supervised Learning]]></title>
            <link>https://velog.io/@jyoung_710/Supervised-Learning</link>
            <guid>https://velog.io/@jyoung_710/Supervised-Learning</guid>
            <pubDate>Wed, 14 Aug 2024 22:16:15 GMT</pubDate>
            <description><![CDATA[<h1 id="span-stylebackground-colore6e6fa-supervised-learning-span"><span style="background-color:#E6E6FA"> Supervised Learning </span></h1>
<p>📖 <span style="background-color:#D3E3F5; font-weight: bold;">지도 학습(Supervised Learning)</span>은 기계 학습 방법 중 하나로, 데이터의 레이블을 기반으로 패턴을 학습하고 예측하는 방법이다.  마치  학생에게 문제지(데이터)와 답안지(레이블)을 모두 주며 학습하도록 하는 방법라고 할 수 있다.</p>
<p>지도 학습 방법에는 대표적으로 분류(Classification), 회귀(Regression)이 있다.</p>
<h2 id="classification">Classification</h2>
<p>🎏 <span style="background-color:#D3E3F5; font-weight: bold;">분류(Classification)</span>는 주어진 데이터들을 클래스 별로 구별하는 것을 말한다. 
<img src="https://velog.velcdn.com/images/jyoung_710/post/4a773245-a419-4d12-a56b-aacc135c1c59/image.png" alt="">
위와 같이 클래스가 여러 개 있을 때 두 클래스를 가장 잘 구분할 수 있는 선을 그려낼 수 있는데, 이것이 분류 문제가 풀고자 하는 것이다. 
주로 예측 결과가 숫자가 아닌 경우에 많이 사용하며, Logictic Regression, Naive Bayes, SVM 등이 분류 문제에 사용된다.</p>
<h2 id="regression">Regression</h2>
<p>📊 <span style="background-color:#D3E3F5; font-weight: bold;">회귀(Regression)</span>는 독립 변수와 종속 변수 간의 관계를 파악하는 것을 말한다.
<img src="https://velog.velcdn.com/images/jyoung_710/post/773328ed-7cfc-482e-8a0e-ce3fc8776fda/image.png" alt="">
위와 같이 데이터들이 나타내는 관계를 가장 잘 나타내는 선을 그려낼 수 있고, 이것이 회귀 문제가 풀고자 하는 것이다. 
주로 예측 결과가 숫자인 경우 많이 사용하며, Linear Regression, Ridge Regression 등이 회귀 문제에 사용된다. </p>
<h3 id="linear-regression">Linear Regression</h3>
<p>👆 <span style="background-color:#D3E3F5; font-weight: bold;">선형 회귀(Linear Regression)</span>는 독립 변수들과 종속 변수 간의 관계를 예측할 때, 그 관계를 선형의 관계로 가정하는 방법이다. 
<img src="https://velog.velcdn.com/images/jyoung_710/post/812e77e1-8d99-45be-b74a-8eb012877adf/image.png" alt="">
위 그림과 같은 상황에서 데이터를 가장 잘 설명해주는 직선은 어떤 직선일까?
이를 구하기 위해 <span style="background-color:#D3E3F5; font-weight: bold;">잔차(Residual)</span>의 제곱 합을 구해야 한다. 
<img src="https://velog.velcdn.com/images/jyoung_710/post/f92d7d26-7f9f-499a-b5fb-37831ef091f8/image.png" alt=""></p>
<p>잔차는 모델의 예측값과 실체 관측값 사이의 차이로, 잔차의 값은 음수가 될 수 있기에 합이 0이 되지 않도록 제곱하여 합하게 된다. 데이터의 관계를 가장 잘 나타내는 직선은 잔차의 절댓값이 작을 것이므로, 해당 직선은 <span style="background-color:#D3E3F5; font-weight: bold;">잔차 제곱의 합이 최소</span>가 되는 직선일 것이다.</p>
<h2 id="classification--regression">Classification &amp; Regression</h2>
<p>분류 문제와 회귀 문제에 모두 사용되는 방법들이 존재한다. 
K-NN, Decision Tree, Random Forests,,, 등이 분류와  회귀에 사용된다.</p>
<h3 id="k-nn">K-NN</h3>
<p>🤝 <span style="background-color:#D3E3F5; font-weight: bold;">K-NN(K-Nearest Neighbor)</span>는 새로운 데이터를 주변의 K개의 이웃 데이터 중 가장 많은 데이터 세트에 할당하는 방법이다.</p>
<p>K-NN의 과정은 아래와 같다.</p>
<hr>
<ol>
<li>새로운 데이터와 다른 데이터 포인트 간의 거리를 모두 계산한다</li>
<li>그중 가까운 이웃 데이터 포인트 K개를 선택한다</li>
<li>이웃 데이터 포인트의 레이블을 기준으로 가장 많은 데이터 레이블에 새로운 데이터를 할당한다</li>
</ol>
<hr>
<p><img src="https://velog.velcdn.com/images/jyoung_710/post/bb83c977-cbfd-49ce-91fa-12feb5f5b6ea/image.png" alt="">
위와 같은 상황이라면 K가 4일 때 회색 데이터는 2번 레이블로 할당해야 한다.</p>
<blockquote>
<p>🙆‍♂️ <span style="background-color:#D3E3F5; font-weight: bold;"> 장점 </span></p>
</blockquote>
<ul>
<li>직관적으로 이해할 수 있다</li>
<li>훈련 과정이 필요하지 않다</li>
<li>다중 클래스에도 효과적이다</li>
</ul>
<blockquote>
<p>🙅‍♂️ <span style="background-color:#ff8a89; font-weight: bold;"> 단점  </span></p>
</blockquote>
<ul>
<li>데이터 규모에 민감하다</li>
<li>특성 스케일링에 민감하다</li>
<li>하이퍼 파라미터를 결정해야 한다</li>
</ul>
<h3 id="decision-tree">Decision Tree</h3>
<p>🌳 <span style="background-color:#D3E3F5; font-weight: bold;">의사결정나무(Decision Tree)</span>는 의사결정 규칙과 그 결과물들을 트리구조로 도식화 한 것을 말한다.</p>
<p> 의사결정 나무의 과정은 다음과 같다.</p>
<hr>
<ol>
<li>데이터 집합의 불순도(Impurity)가 가장 낮아지도록 하는 분기점을 선택한다<blockquote>
<p><span style = 'background-color:#ECE4E2; border-radius: 30%'>🎊</span> <strong>불순도(Impurity)</strong>
📌 <span style="background-color:#D3E3F5; font-weight: bold;">엔트로피(Entropy)</span>
: 데이터 집합의 불확실성이나 무질서도를 측정하는 값</br>
정보량은 로그를 활용하여 아래와 같이 정의한다.
<img src="https://velog.velcdn.com/images/jyoung_710/post/f6d6c9a2-c399-42c0-90cb-5a55acebb4e4/image.png" alt="">
확률값에 반비례 해야 하고, 두 사건의 정보량 합이 각 사건의 정보량 합과 같아야 하기 때문에 로그를 사용한다.
엔트로피는 이러한 정보량의 평균이므로 아래와 같이 정의할 수 있다.
<img src="https://velog.velcdn.com/images/jyoung_710/post/944d4220-11cc-43d2-a6b2-767fe0a28709/image.png" alt="">
위 식에 따라 확률 분포가 균일할수록 엔트로피가 높아진다.</br>
📌 <span style="background-color:#D3E3F5; font-weight: bold;">정보 이득(Information Gain)</span>
: 특정 분할에 의해 얻어지는 순도의 증가/불순도의 감소</br>
정보 이득은 이전(상위 노드) 엔트로피의 값을 현재(하위 노드) 엔트로피의 값으로 뺀 것이다. 
<img src="https://velog.velcdn.com/images/jyoung_710/post/da6db4ba-6b82-4bf5-a97f-2150ba4dd3f0/image.png" alt="">
따라서 불순도가 가장 낮아지는 분기점은 순도가 가장 높은, 즉 정보의 이득이 가장 많은 분기점으로 해석할 수 있다. 또한 상위 노드에서의 엔트로피와 하위 노드에서의 엔트로피의 차가 가장 커져야 하므로, 분기점을 통해 데이터를 분할할 때 엔트로피가 낮아져야 함을 의미한다.</p>
</blockquote>
</li>
</ol>
<ol start="2">
<li>데이터를 분할하고 결정 노드를 생성하여 정지 기준을 충족할 때까지 의사결정나무를 계속 구축한다</li>
</ol>
<hr>
<p> <img src="https://velog.velcdn.com/images/jyoung_710/post/020957f1-5fc6-413e-9270-a24ec4a4c7d2/image.png" alt="">
 위 그림에서 볼 수 있듯 트리가 점점 깊어지면서 데이터가 잘 분류되고 있는 것을 볼 수 있다. 하지만 트리의 깊이가 너무 깊어지게 되면 오히려 <span style="background-color:#D3E3F5; font-weight: bold;">과적합(overfitting)</span>되어 새로운 데이터에 안좋은 성능을 낼 가능성이 매우 높다.</p>
<blockquote>
<p>🙆‍♂️ <span style="background-color:#D3E3F5; font-weight: bold;"> 장점 </span></p>
</blockquote>
<ul>
<li>해석/설명이 가능하다</li>
<li>데이터 전처리가 간단하다</li>
<li>비선형 관계도 포착할 수 있다</li>
</ul>
<blockquote>
<p>🙅‍♂️ <span style="background-color:#ff8a89; font-weight: bold;"> 단점  </span></p>
</blockquote>
<ul>
<li>과적합 경향이 있다</li>
<li>작은 데이터 변화에 민감하다</li>
<li>최적화가 어렵다</li>
</ul>
<h1 id="span-stylebackground-colore6e6faconfusion-matrixspan"><span style="background-color:#E6E6FA">Confusion Matrix</span></h1>
<p>🔢 <span style="background-color:#D3E3F5; font-weight: bold;">혼동 행렬(Confusion Matrix)</span>는 분류 문제에서 실제 값과 예측 값을 행렬 형태로 표현한 것이다. 
<img src="https://velog.velcdn.com/images/jyoung_710/post/2802d229-c39f-4ff7-b417-d29901fc7462/image.png" alt="">
위의 그림과 같이 복잡하게 생겼다. Positive로 예측했는데 실제로 Positive면 True Positive(TP), Positive로 예측했는데 사실 Negative면 False Positive(FP)인 것이다.</p>
<p>이를 바탕으로 한 평가 지표로써 정밀도와 재현율이 존재한다.
😎 <span style="background-color:#D3E3F5; font-weight: bold;">정밀도(Precision)</span>는 양성으로 판단한 것 중 실제로 양성인 것의 비율이다.
<img src="https://velog.velcdn.com/images/jyoung_710/post/c680842e-2d44-4013-b8aa-a33b343e446e/image.png" alt="">
🤩 <span style="background-color:#D3E3F5; font-weight: bold;">재현율(Recall)</span>은 실제 양성인 것 중 양성으로 판단한 비율이다.
<img src="https://velog.velcdn.com/images/jyoung_710/post/d63c7112-cf84-42c6-bab7-d6d9edd03fbd/image.png" alt="">
재현율을 다른 관점으로 보면, 양성에 대해 얼마나 &#39;민감&#39;하게 반응하는가로 해석할 수도 있다.</p>
<p>만일 정밀도와 재현율 모두 중요하다면, 정밀도와 민감도의 조화평균인 🚗 <span style="background-color:#D3E3F5; font-weight: bold;">F1-Score</span>를 사용할 수도 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Unsupervised Learning]]></title>
            <link>https://velog.io/@jyoung_710/%EB%B9%84%EC%A7%80%EB%8F%84-%ED%95%99%EC%8A%B5</link>
            <guid>https://velog.io/@jyoung_710/%EB%B9%84%EC%A7%80%EB%8F%84-%ED%95%99%EC%8A%B5</guid>
            <pubDate>Wed, 14 Aug 2024 19:52:40 GMT</pubDate>
            <description><![CDATA[<h1 id="span-stylebackground-colore6e6famachine-learningspan"><span style="background-color:#E6E6FA;">Machine Learning</span></h1>
<p>🤖 <span style="background-color:#D3E3F5; font-weight: bold;">기계학습</span>은 기계가 스스로 데이터 속의 규칙성을 학습해 패턴을 발견하는 알고리즘이다. 
<img src="https://velog.velcdn.com/images/jyoung_710/post/7a63792f-4242-43a5-a7f4-30c0ae6fd57a/image.png" alt="">
우리가 흔히 말하는 머신러닝, 딥러닝은 인공지능이라는 큰 범주에 포함된 개념으로, 인공지능을 구현하기 위한 방법이라고 생각하면 된다. 특히 딥러닝은 기계학습의 인공신경망이라는 방법론에서 진화한 기술로, 신경망을 여러 계층 쌓아 만들어 &#39;Deep&#39; Learning이라고 한다. </p>
<p>데이터 분석 과정은 크게 [ 문제 정의 =&gt; 데이터 수집 및 전처리 =&gt; EDA =&gt; 모델링 =&gt; 평가 ] 의 과정을 거치는데, &#39;모델링&#39; 과정에 기계학습이 사용된다.</p>
<p>기계학습은 크게 아래의 세 가지로 분류된다.
<img src="https://velog.velcdn.com/images/jyoung_710/post/25ab41e3-a26c-430c-afca-9e7ad86fd4a8/image.png" alt=""></p>
<p>📖 <span style="background-color:#D3E3F5; font-weight: bold;">지도 학습(Supervised Learning)</span>은 데이터의 레이블을 기반으로 패턴을 학습하고 예측하는 방법이다. 
이는 선생이 학생에게 구몬 학습지(데이터)와 정답지(레이블)을 모두 주고 잘 학습해보라고 하는 것과 같다. 
지도학습의 대표적인 방법으로는 분류(Classification), 회귀(Regression)가 있다.</p>
<p>☕ <span style="background-color:#D3E3F5; font-weight: bold;">비지도 학습(Unsupervised Learning)</span>은 데이터의 레이블 없이 데이터의 특성 분포를 파악하는 방법이다. 
이는 선생이 학생에게 답안지(레이블)를 주지 않고 학습지(데이터)만 준 뒤 알아서 공부하라고 하는 것과 같다. 
비지도 학습의 대표적인 방법으로는 군집화(Clustering), 차원 축소(Demesion Reduction)가 있다.</p>
<p>🎮 <span style="background-color:#D3E3F5; font-weight: bold;">강화 학습(Reinforcement Learning)</span>은 기계가 주어진 환경 내에서 보상을 최대화하는 행동을 선택하는 방법이다.
간단히 말해 기계가 잘하면 당근🥕을, 못하면 채찍🩼을 주는 것이다.</p>
<h1 id="span-stylebackground-colore6e6faunsupervised-learningspan"><span style="background-color:#E6E6FA">Unsupervised Learning</span></h1>
<blockquote>
<p><span style = 'background-color:#ECE4E2; border-radius: 30%'>🧐</span> 정답이 없는데 어떻게 학습하고, 어떻게 맞혀요,,,?</p>
</blockquote>
<h2 id="clustering">Clustering</h2>
<p><img src="https://velog.velcdn.com/images/jyoung_710/post/5a65607e-22d3-4ba8-b204-3e622932bf25/image.png" alt="">
위와 같은 데이터 분포가 존재하고 우리는 해당 데이터 셋을 세 개의 그룹으로 묶어야 한다. 
그렇다면 우리는 당연하게도 아래와 같이 그룹을 지을 것이다.
<img src="https://velog.velcdn.com/images/jyoung_710/post/c997c8e1-d01f-46a6-b648-9eede18f5458/image.png" alt="">
이렇게 우리는 정답, 즉 레이블 없이 데이터의 특성 분포를 파악할 수 있다. </p>
<p>하지만 정답이 존재하지 않으므로 아래와 같이 빨간색으로 표시한 데이터를 어디로 분류해야 하는지에 대한 정답도 없다. 다만 데이터에 대한 특성들이 더 존재한다면 지금보다 더 확실하게 구분할 수 있을 것이다.
<img src="https://velog.velcdn.com/images/jyoung_710/post/10c3ff48-db08-47e7-bcc7-ba342b200857/image.png" alt=""></p>
<p>이처럼 데이터의 특성들을 바탕으로 데이터의 그룹을 만드는 것을 <span style="background-color:#D3E3F5; font-weight: bold;">군집화(Clustering)</span>이라고 한다.
군집화의 대표적인 방법으로는 K-Means, KNN 등이 있다.</p>
<h3 id="k-means">K-Means</h3>
<p>🎈 <span style="background-color:#D3E3F5; font-weight: bold;">K-Means</span>는 데이터를 K개의 클러스터로 묶는 방법이다. </p>
<p>K-Means 과정은 다음과 같다.</p>
<hr>
<ol>
<li><span style="background-color:#D3E3F5; font-weight: bold;">K개의 점</span>을 랜덤하게 선택한다.</li>
<li>선택한 점을 중심점(Centroid)로 설정하고, 데이터들을 가장 가까운 중심점을 기준으로 할당한다.</li>
<li>각 군집 내의 <span style="background-color:#D3E3F5; font-weight: bold;">데이터들의 평균점</span>을 새로운 중심점으로 갱신한다.</li>
<li>중심점이 더 이상 갱신되지 않을 때까지 2~3단계를 반복한다.</li>
</ol>
<hr>
<p><img src="https://velog.velcdn.com/images/jyoung_710/post/0647cdae-65bf-4480-ae4a-58074db081fe/image.png" alt="">
반복 횟수가 늘어날수록 중심점이 적절한 군집의 중심으로 찾아가는 것을 볼 수 있다. </p>
<blockquote>
<p>🙆‍♂️ <span style="background-color:#D3E3F5; font-weight: bold;"> 장점 </span></p>
</blockquote>
<ul>
<li>직관적으로 이해할 수 있다</li>
<li>연산 속도가 빠르다</li>
<li>수렴성이 보장된다</li>
</ul>
<blockquote>
<p>🙅‍♂️ <span style="background-color:#ff8a89; font-weight: bold;"> 단점  </span></p>
</blockquote>
<ul>
<li>군집의 개수를 직접 설정해야 한다</li>
<li>범주형 데이터에는 활용하기 어렵다</li>
<li>노이즈와 이상치에 민감하다</li>
<li>초기 중심점에 대한 의존도가 높다</li>
</ul>
<h3 id="dbscan">DBSCAN</h3>
<p>👥 <span style="background-color:#D3E3F5; font-weight: bold;">DBSCAN(Density-Based Spatial Clustering of Applications with Noise)</span>은 밀도를 기반으로 군집을 나누는 방법이다. </p>
<p>DBSCAN의 과정은 다음과 같다.</p>
<hr>
<ol>
<li>주어진 거리 범위(Epsilon) 내에서 포인트를 찾는다</li>
<li>최소 포인트 개수를 만족한다면 해당 포인트를 Core Point로 설정한다</li>
<li>거리 범위 내의 포인트 중에서 포인트를 새로 설정하고 1~2의 과정을 반복한다</li>
<li>거리 범위 내에서 최소 포인트 개수를 충족하지 못한다면 Border Point로 지정한다</li>
<li>모든 포인트에 대해서 1~4 과정을 반복한다</li>
<li>어느 클러스터에도 포함되지 않는 포인트는 Noise Point로 처리한다
<img src="https://velog.velcdn.com/images/jyoung_710/post/9f896019-9854-4fae-a6cc-0a35eef0e3f9/image.png" alt=""></li>
</ol>
<hr>
<p><img src="https://velog.velcdn.com/images/jyoung_710/post/f17e4a6e-9680-45a8-99f4-ce31d3502f29/image.png" alt="">
이처럼 DBSCAN을 시행할 경우 가까이 뭉쳐있는 데이터 포인트끼리 하나의 군집을 이루게 된다.</p>
<blockquote>
<p>🙆‍♂️ <span style="background-color:#D3E3F5; font-weight: bold;"> 장점 </span></p>
</blockquote>
<ul>
<li>다양한 클러스터를 식별할 수 있다</li>
<li>이상치에 대응이 가능하다</li>
<li>군집 개수를 지정할 필요가 없다</li>
<li>안정적인 클러스터가 도출된다</li>
</ul>
<blockquote>
<p>🙅‍♂️ <span style="background-color:#ff8a89; font-weight: bold;"> 단점  </span></p>
</blockquote>
<ul>
<li>고차원 데이터에서 활용이 어렵다 (차원의 저주)</li>
<li>데이터의 밀도가 일정하지 않을 경우 제대로 군집화가 안된다</li>
<li>하이퍼 파라미터에 민감하다</li>
<li>클러스터의 경계가 모호하다</li>
</ul>
<h2 id="demension-reduction">Demension Reduction</h2>
<p><img src="https://velog.velcdn.com/images/jyoung_710/post/86a04648-12c2-4f4a-a9bf-f863c054ff35/image.png" alt="">
위와 같은 3차원의 데이터를 분석하는 것은 2차원보다는 당연히 복잡할 것이다. 그러나 우리가 앞으로 분석하게 될 데이터들은 3차원을 훌쩍 뛰어넘는 고차원의 데이터들이다😨
우리가 고차원의 데이터를 분석하는 것이 어렵기에 데이터의 특성을 최대한 반영한 채로 차원을 낮춰 분석하는데, 이것을 <span style="background-color:#D3E3F5; font-weight: bold;">차원 축소(Demension Reduction)</span>라고 한다.</p>
<p>추가적으로 차원이 늘어날수록 데이터의 밀도가 낮아지게 되고, 이에 따라 모델의 성능이 급격하게 저하될 수 있다. 이를 <span style="background-color:#D3E3F5; font-weight: bold;">차원의 저주(The Curse of Demensionality)</span>라고 하며, 차원 축소는 차원의 저주를 막기 위해서도 사용된다.</p>
<p>차원 축소의 방법에는 PCA, t-SNE 등이 있다.</p>
<h3 id="pca">PCA</h3>
<p>🔨 <span style="background-color:#D3E3F5; font-weight: bold;">PCA(Principal Component Analysis)</span>는 데이터의 정보를 최대한 보존하는 축에 데이터를 투영(Projection)하여 차원을 축소하는 방법이다.</p>
<p>다음은 PCA의 과정이다.</p>
<hr>
<ol>
<li>데이터 표준화하기</li>
<li>표준화한 데이터의 공분산 행렬 구하기</li>
<li>공분산 행렬의 eigenstuff(eigenvector, eigenvalue) 구하기</li>
<li>eigenstuff를 eigenvalue가 큰 것부터 작은 순서대로 정렬하기</li>
<li>원하는 차원만큼의 상위 eigenvector만을 가지고 데이터를 투영하기 
<img src="https://velog.velcdn.com/images/jyoung_710/post/90b7284c-9c78-4166-a83c-0b6da1a229b3/image.png" alt=""></li>
</ol>
<hr>
<p><img src="https://velog.velcdn.com/images/jyoung_710/post/5b506ef1-88bd-4491-b875-068a59c20e36/image.png" alt="">
복잡한 3차원의 데이터를 2차원으로 PCA를 통해 차원 축소하였더니 데이터의 분포를 분석하기 더 쉬워졌다.</p>
<blockquote>
<p>🙆‍♂️ <span style="background-color:#D3E3F5; font-weight: bold;"> 장점 </span></p>
</blockquote>
<ul>
<li>차원 축소를 통한 직관적인 시각화 가능</li>
<li>데이터 압축</li>
<li>노이즈 제거 가능</li>
</ul>
<blockquote>
<p>🙅‍♂️ <span style="background-color:#ff8a89; font-weight: bold;"> 단점  </span></p>
</blockquote>
<ul>
<li>비선형적 데이터에 적합하지 않음</li>
<li>정보 손실로 인한 성능 저하 가능성 있음</li>
<li>데이터 축이 달라져 결과 해석에 어려움 있을 수도 있음</li>
</ul>
<h1 id="span-stylebackground-colore6e6fapracticespan"><span style="background-color:#E6E6FA">Practice</span></h1>
<blockquote>
<p><span style = 'background-color:#ECE4E2; border-radius: 30%'>🧌</span> 포켓몬 데이터를 분석해보자!</p>
</blockquote>
<p><a href="https://pokemondb.net/pokedex/all">https://pokemondb.net/pokedex/all</a>
위 사이트는 포켓몬 데이터를 정리해놓은 아주 좋은 사이트다.
<img src="https://velog.velcdn.com/images/jyoung_710/post/efd372ed-a0d9-4cf0-a922-fc79300922b4/image.png" alt="">
해당 사이트에서 포켓몬 데이터를 불러와 데이터 프레임으로 나타낸다.</p>
<pre><code class="language-python">from bs4 import BeautifulSoup
import requests
import pandas as pd
from io import StringIO # table을 Data Frame으로 바꿔줌

html = requests.get(&quot;https://pokemondb.net/pokedex/all&quot;).text
soup = BeautifulSoup(html, &#39;html_parser&#39;)

html_table = soup.find(&#39;table&#39;)
html_table_str = str(html_table)
html_table_io = StringIO(html_table_str)

df = pd.read_html(html_table_io)[0] #표를 리스트 형태로 반환하므로 인덱싱을 해야 함
print(df)</code></pre>
<p><img src="https://velog.velcdn.com/images/jyoung_710/post/210462d6-c774-4776-b03b-73fe82902af1/image.png" alt="">
야무지게 잘 됐다😎,,,고 생각했지만 Type 컬럼을 보면 타입이 두 개인 포켓몬은 &#39;Grass Poison&#39;과 같이 하나의 문자열로 되어있다. 이는 데이터를 분석할 때 정확한 분석을 방해하므로 타입을 분리해야 한다.</p>
<pre><code class="language-python">type_ = df[&#39;Type&#39;].str.split()
df[&#39;Type_1&#39;] = type_.str[0]
df[&#39;Type_2&#39;] = type_.str[1]

print(df.info())</code></pre>
<p><img src="https://velog.velcdn.com/images/jyoung_710/post/8cd510cf-b2b8-44e2-ba40-3c2e0f63fa02/image.png" alt="">
다시 데이터를 보면 타입이 잘 분리된 것을 볼 수 있다. Type_2에 Null 값이 발생한 것은 모든 포켓몬이 타입을 두 개 갖고 있는 것이 아니기 때문이다.</p>
<p>이제 데이터를 시각화하여 나타내보자.</p>
<pre><code class="language-python">import matplotlib.pyplot as plt
import seaborn as sns

type_list = list(en_df[&#39;Type_1&#39;].unique())

plt.figure(figsize = (10,3), dpi=150)
plt.xticks(rotation=45, ha = &#39;right&#39;)
sns.boxplot(x = &#39;Type_1&#39;, y = &#39;Total&#39;, data = en_df, order = type_list)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/jyoung_710/post/9b3af87a-aaa3-4738-98e1-3d51a6de0814/image.png" alt="">
가로 축을 Type_1, 세로 축을 Total로 했을 때의 Box Plot이다. 독타입에 존재하는 괴랄한 이상치를 제외하고 데이터가 대체적으로 균등하게 분포되어 있는 것 같다.</p>
<p>_<span style='color:gray'>저 괴랄한 이상치 포켓몬을 찾아보니 Eternatus라는 무한다이노라는 포켓몬이라고 한다,, 저런 밸런스 붕괴 포켓몬이 존재해도 되는건가,,,</span>_
<img src="https://velog.velcdn.com/images/jyoung_710/post/fb320bc2-9aa7-4993-8ad5-a0f36cfb1a7b/image.png" alt=""></p>
<pre><code class="language-python">plt.figure(figsize = (10,3), dpi=150)
plt.xticks(rotation=45, ha = &#39;right&#39;)
sns.boxplot(x = &#39;Type_2&#39;, y = &#39;Total&#39;, data = df, order = type_list)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/jyoung_710/post/aac486cd-8a8f-4a0c-8af0-a953811b67e2/image.png" alt="">
Type_2도 메가 어쩌구 빼고는 비슷비슷하게 분포되어 있는 듯 하다.</p>
<p>이번엔 모든 능력치 데이터의 Scatter Plot과 히스토그램을 봐보자.</p>
<pre><code class="language-python">sns.pairplot(df[[&#39;HP&#39;, &#39;Attack&#39;, &#39;Defense&#39;, &#39;Sp. Atk&#39;, &#39;Sp. Def&#39;, &#39;Speed&#39;]])
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/jyoung_710/post/8d66c1cb-1f31-4b92-b324-31ea3fc4ea04/image.png" alt="">
살펴보니 히스토그램은 약간의 positive skewness를 갖는 경향이 있는 것 같고 Scatter Plot도 능력치끼리 양의 상관관계를 일부 보이는 듯하다.</p>
<p>이제 본격적으로 비지도 학습을 시켜보겠다. 
포켓몬의 능력치를 특성값으로 사용할 것이고, 데이터 군집화를 하기 전 차원 축소를 해줄 것이다.
<span style='color:gray'>(PCA를 사용하려 했으나 PCA보다 t-SNE의 결과가 더 군집화를 실습하기에 적합하여 t-SNE로 차원축소를 진행했다. 
  t-SNE는 큰 틀로만 이해해서 나중에 PCA와 함께 자세히 공부할 필요가 있겠다.)</span></p>
<pre><code class="language-python">from sklearn.manifold import TSNE

tsne = TSNE(n_components=2, learning_rate = 1000, random_state = 928)
result_tsne = tsne.fit_transform(df.iloc[:,4:10])
df[[&#39;X&#39;, &#39;Y&#39;]] = result_tsne

sns.scatterplot(x = &#39;X&#39;, y = &#39;Y&#39;, data = df)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/jyoung_710/post/973665f9-5a4d-4d9f-b147-13565b16b08e/image.png" alt="">
오호,, 뭔가 군집화하고 싶게 생겼다.</p>
<p>우선 K-Means의 K값에 따른 군집화 결과를 보자.</p>
<pre><code class="language-python">fig, ax = plt.subplots(ncols=3, nrows=2, figsize=(15,8))
fig.subplots_adjust(hspace=0.3)

for i, cl in zip(range(6), range(3,9)):
  kmeans = KMeans(n_clusters=cl, random_state=928, n_init = &#39;auto&#39;)
  label = kmeans.fit_predict(df[[&#39;X&#39;, &#39;Y&#39;]])
  df[&#39;Cluster&#39;] = label
  sns.scatterplot(x = &#39;X&#39;, y = &#39;Y&#39;, hue = &#39;Cluster&#39;, data = df, ax=ax[int(i/3),int(i%3)])
  ax[int(i/3), int(i%3)].set_title(f&#39;Cluster = {cl}&#39;)

plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/jyoung_710/post/79b16bbc-63b1-461c-a60d-2bc7ce88f715/image.png" alt="">
K값이 3 또는 4일 때 데이터를 가장 잘 군집화 한 것 같다. K가 5 이상일 때부터 하나의 군집에 묶여있어야 할 데이터가 억지로 나누어지는 모습을 보인다.</p>
<p>다음으로 DBSCA의 epsilon 값에 따른 군집화 결과를 나타내보자.</p>
<pre><code class="language-python">import numpy

fig, ax = plt.subplots(ncols=3, nrows=2, figsize=(15,8))
fig.subplots_adjust(hspace=0.3)

for i, ep in zip(range(6), numpy.arange(2,5,0.5)):
  cluster = DBSCAN(eps = ep, min_samples=5)
  label = cluster.fit_predict(df[[&#39;X&#39;, &#39;Y&#39;]])
  df[&#39;Cluster&#39;] = label
  sns.scatterplot(x = &#39;X&#39;, y = &#39;Y&#39;, hue = &#39;Cluster&#39;, data = df, ax=ax[int(i/3),int(i%3)])
  ax[int(i/3), int(i%3)].set_title(f&#39;Eps = {ep}&#39;)

plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/jyoung_710/post/c3e24d53-df2e-413c-9f42-64698323a019/image.png" alt="">
DBSCAN은 epsilon이 3.5일 때 그나마 가장 뭉쳐있는 데이터끼리 군집을 이룬 것 같다. </p>
<p>확실히 비교해보니 K-Means와 DBSCAN의 차이가 명확히 보인다. K-Means는 피자 자르듯 칼로 뚝뚝 잘라낸 느낌이라면 DBSCAN은 가위로 적절히 오려낸 느낌이다. 두 방법의 특성이 잘 드러난 듯 하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Dynamic Crawling]]></title>
            <link>https://velog.io/@jyoung_710/Dynamic-Crawling</link>
            <guid>https://velog.io/@jyoung_710/Dynamic-Crawling</guid>
            <pubDate>Tue, 13 Aug 2024 19:40:03 GMT</pubDate>
            <description><![CDATA[<h1 id="span-stylebackground-colore6e6fa-static-vs-dynamic-span"><span style="background-color:#E6E6FA"> Static vs Dynamic </span></h1>
<hr>
<p>🪨 <span style="background-color:#D3E3F5; font-weight: bold;">정적(Static) 크롤링</span>은 말 그대로 정적인 데이터를 크롤링하는 것을 말한다. 이때 정적인 데이터는 변하지 않는 데이터로, 웹 페이지 상에서 <span style="background-color:#D3E3F5; font-weight: bold;">url의 변화없이</span> 그대로 수집가능한 데이터이다. 
🏃‍♂️ <span style="background-color:#D3E3F5; font-weight: bold;">동적(Dynamic) 크롤링</span>은 정적 크롤링과 달리 <span style="background-color:#D3E3F5; font-weight: bold;">url의 변화가 필요한</span> 데이터를 크롤링하는 방법을 말한다. 만일 네이버 메일 내용을 크롤링해야 한다면 네이버 로그인이 필요하고, 이때 로그인 후 메일 내용을 크롤링하는 과정이 동적 크롤링인 것이다. url이 고정되지 않고 변화함으로 정적 크롤링에 비해 상대적으로 오래 걸린다.</p>
<hr>
<h1 id="span-stylebackground-colore6e6faseleniumspan"><span style="background-color:#E6E6FA">Selenium</span></h1>
<p>Selenium은 웹 어플리케이션 테스트를 자동화하기 위해 고안된 프레임워크이지만, 동적 웹 크롤링에서 웹을 동작시키는 데에 매우 유용하다. 동적 크롤링을 할 시에 로그인을 해야하는 경우, 마우스 호버(Hover)나 스크롤 다운 등의 웹의 동작으로만 정보가 등장하는 경우에 selenium을 사용하면 된다. </p>
<p>다음은 selenium에서 자주 사용되는 것들이다.</p>
<blockquote>
<p>📌 <strong>.get(URL)</strong>
해당 URL로 이동한다.
📌 <strong>.find_element(By.~, &quot; &quot;)</strong>
~에는 CLASS_NAME, ID, CSS_SELECTOR, TAG_NAME, XPATH 등의 HTML 태그 속성(attribute)이 쓰일 수 있고 &quot; &quot;에는 해당 속성의 값(value)가 쓰인다. 해당 메서드는 인자로 받은 조건을 바탕으로 요소를 찾는 역할을 한다.
만약 ID가 &#39;jyoung&#39;인 요소를 찾아 선택하고 싶다면 아래와 같이 작성하면 된다.</p>
</blockquote>
<pre><code class="language-python">jyoung = driver.find_element(By.ID, &quot;jyoung&quot;)</code></pre>
<p>📌 <strong>.click()</strong>
말 그대로 요소를 클릭해준다. 
앞선 예시의 jyoung 요소를 클릭하고 싶다면 아래와 같이 작성하면 된다.</p>
<pre><code class="language-python">jyoung.click()</code></pre>
<p>📌 <strong>.send_keys(key)</strong>
요소에 정보를 보내주는 역할을 한다. 
만약 id가 &#39;search_bar&#39;인 검색창에 &#39;딥다이브&#39;를 검색하고자 한다면 아래와 같이 작성하면 된다.</p>
<pre><code class="language-python">search = driver.find_element(By.ID, &#39;search_bar&#39;)
search.send_keys(&#39;딥다이브&#39;)</code></pre>
<p>📌 <strong>.page_source</strong>
웹 페이지의 html 소스 코드를 가지고 오는 역할을 수행한다.</p>
<hr>
<blockquote>
<p><span style = 'background-color:#ECE4E2; border-radius: 30%'>❓</span> selenium으로 소스코드를 가져올 수 있는 거라면 굳이 requests를 사용해야 할까요?</p>
</blockquote>
<p>실제로 Selenium만 가지고도 웹 크롤링은 가능하다. 그러나 Selenium은 무겁기에 반복 작업이 많아질수록 속도가 <span style="background-color:#D3E3F5; font-weight: bold;">매우 느려지는 단점</span>을 가지고 있다. 따라서 최대한 Selenium을 동적 크롤링을 위한 도구로써만 사용하며 필요에 따라 적절히 혼용해 사용하는 것이 좋을 듯하다.</p>
<hr>
<h1 id="span-stylebackground-colore6e6fa-practice-span"><span style="background-color:#E6E6FA"> Practice </span></h1>
<blockquote>
<p><span style = 'background-color:#ECE4E2; border-radius: 30%'>💡</span> 파리 2024 올림픽 네이버 뉴스 기사 제목을 수집해보자!</p>
</blockquote>
<p>우선 필요한 프레임워크, 라이브러리, 모듈을 불러오자.</p>
<pre><code class="language-python">from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from bs4 import BeautifulSoup
import requests
import sys
import os
import time</code></pre>
<p>동적 웹 크롤링을 위해 webdriver를 통해 크롬을 열어줘야 한다.
webdriver는 파이썬으로 웹 브라우저를 제어할 수 있도록 해준다.</p>
<pre><code class="language-python">driver = webdriver.Chrome()</code></pre>
<p>해당 코드를 실행시키면 자동화된 소프트웨어로 제어된다는 문구가 나오며 크롬이 자동으로 열리게 된다.
<img src="https://velog.velcdn.com/images/jyoung_710/post/f3602907-6e77-48d7-a860-64c2b43d1312/image.png" alt="webdriver로 연 chrome"></p>
<p>다음으로 드라이버로 파리올림픽 뉴스 페이지(<a href="https://m.sports.naver.com/paris2024/news?date=20240803&amp;sort=popular&amp;isPhoto=N">https://m.sports.naver.com/paris2024/news?date=20240803&amp;sort=popular&amp;isPhoto=N</a>)로 접속 후 페이지 소스 코드를 불러오자.</p>
<pre><code class="language-python">url = &quot;https://m.sports.naver.com/paris2024/news?date=20240803&amp;sort=popular&amp;isPhoto=N&quot;
driver.get(url)
html = driver.page_source</code></pre>
<p>아주 잘 접속한 것을 볼 수 있다😎
<img src="https://velog.velcdn.com/images/jyoung_710/post/9bb62bce-10de-4a79-939c-87ccd5015f1f/image.png" alt="파리올림픽 뉴스 사이트로 들어간 사진">
해당 웹페이지를 개발자도구로 살펴보면
<img src="https://velog.velcdn.com/images/jyoung_710/post/86078714-1575-4d48-806a-28a6f379c556/image.png" alt="파리올림픽 뉴스 사이트 개발자 도구">
class가 &quot;NewsList_news_list__1QHBd&quot;인 ul 태그 안에 class가 &quot;NewsList_list_item__gQUBA&quot;인 li 태그들이 담겨있는 것을 볼 수 있다. 이 li 태그 안에 뉴스 기사 정보가 담겨있는 것이다. 
여기서 뉴스 기사 제목을 수집하는 과정은 다음과 같다.</p>
<hr>
<ol>
<li>앞서 가져온 소스코드를 활용하여 BeautifulSoup 객체 생성</li>
<li>뉴스 기사를 담은 li 태그들을 감싼 ul 태그를 find로 선택</li>
<li>선택한 ul에서 li 태그들을 find_all로 모두 가져오기</li>
<li>map 함수를 활용하여 제목만을 추출후 리스트에 저장</li>
</ol>
<hr>
<pre><code class="language-python"># 앞서 가져온 소스코드를 활용하여 BeautifulSoup 객체 생성
soup = BeautifulSoup(html, &#39;html.parser&#39;)

# 뉴스 기사를 담은 li 태그들을 감싼 ul 태그를 find로 선택
news_section = soup.find(&#39;ul&#39;,&#39;NewsList_news_list\__1QHBd&#39;)

# 선택한 ul에서 li 태그들을 find_all로 모두 가져오기
news_tag_list = news_section.find_all(&#39;li&#39;, &#39;NewsList_list_item__gQUBA&#39;)

# map 함수를 활용하여 제목만을 추출후 리스트에 저장
news_title_list = list(map(lambda x: x.find(&#39;span&#39;, &#39;NewsList_title__DgFuU&#39;).text, news_tag_list))</code></pre>
<pre><code>[&#39;&quot;상대 선수가 선 넘었다&quot; 은메달 따고 눈물 \&#39;펑펑\&#39; 김민종에 쏟아진 위로·응원&#39;,
 &#39;&quot;왜 우리만 의심하나&quot; 中선수 폭발…파리서도 미∙중 갈등, 무슨 일&#39;,
 &#39;“상대 선수, 선넘었다”…은메달 따고 눈물 펑펑 김민종에 쏟아진 응원&#39;,
 &quot;금메달 따고 동성 연인에 달려가 쪽…伊유도선수 &#39;깜짝 세리머니&#39;&quot;,
 &#39;“올림픽 정신은 어디로?”...징계받은 조지아 유도 선수, 대체 어땠길래&#39;,
 &quot;신유빈 &#39;천적&#39; 잡고 동메달 기회…&#39;손목 부상&#39; 하야타, 기권 가능성도&quot;,
 &#39;금메달 딴 뒤 짝꿍에 청혼…한국 꺾은 중 배드민턴 혼복 선수에 환호&#39;,
 &#39;\&#39;한 발 0점\&#39; 퇴장마저 극적... 김예지 &quot;빅이벤트 선사해 실망 크셨을 것&quot;&#39;,
 &#39;“돌아가신 엄마 폰에 난 ‘금메달리스트’”...정나은, 銀 걸고 눈물&#39;,
 &#39;“왜 나만 갖고 그래”…동메달 딴 中 미모의 수영선수 ‘울분’, 무슨일이&#39;]</code></pre><p>잘 된 것만 같지만 이런 의문이 들어야 한다.</p>
<blockquote>
<p><span style = 'background-color:#ECE4E2; border-radius: 30%'>🧐</span> 왜 기사 개수가 10개 밖에 없지,,,?</p>
</blockquote>
<p>다시 웹페이지로 돌아가보면 
<img src="https://velog.velcdn.com/images/jyoung_710/post/da4b8db5-4820-4a16-998b-390ba372733d/image.png" alt="">
어이쿠,,,🫨 뉴스 더보기를 누르지 않으면 우선 10개의 기사만을 가져오고, 더보기를 눌러야 기사 10개를 더 볼 수 있다. 따라서 기사 50개를 보고싶다면 더보기 버튼을 4번을 눌러야 하는 것이다.😭
이때 ⭐<span style="background-color:#D3E3F5; font-weight: bold;">동적 크롤링</span>⭐이 필요한 것이다!!!</p>
<p>더보기 버튼의 button 태그를 개발자 도구에서 우클릭하면 XPATH를 복사할 수 있고, 우리는 이 XPATH로 버튼을 선택할 것이다.
<img src="https://velog.velcdn.com/images/jyoung_710/post/d5a96af8-1897-4266-ba67-a860907ac1aa/image.png" alt=""></p>
<pre><code class="language-python">news_more_button = driver.find_element(By.XPATH, &quot;//*[@id=&quot;content&quot;]/div[2]/div/div[1]/div[1]/div[2]/button&quot;)</code></pre>
<p>마지막으로 버튼을 5번 클릭한 후의 크롤링 결과를 보고 마무리하겠다.</p>
<pre><code class="language-python">n = 0
for i in range(5):
    news_more_button.click()
    time.sleep(1)
    n+=1
    print(f&#39;{n}회 클릭했습니다.&#39;)

html = driver.page_source 
soup = BeautifulSoup(html, &#39;html.parser&#39;)

news_section = soup.find(&#39;ul&#39;, &#39;NewsList_news_list\__1QHBd&#39;)
news_tag_list = news_section.find_all(&#39;li&#39;, &#39;NewsList_list_item__gQUBA&#39;)

news_title_list = list(map(lambda x: x.find(&#39;span&#39;, &#39;NewsList_title__DgFuU&#39;).text, news_tag_list))

print(f&#39;크롤링한 뉴스 기사의 개수: {len(news_title_list)}&#39;) #60</code></pre>
<blockquote>
<p><span style = 'background-color:#ECE4E2; border-radius: 30%'>🚨</span> time.sleep()이 필요한 이유
웹 페이지의 내용이 바뀔 때, 문서의 정보가 순식간에 바뀌는 것이 아니라 웹 서버와의 통신 과정에서 <span style="background-color:#D3E3F5; font-weight: bold;">작은 딜레이</span>가 발생한다. 따라서 동적 웹 크롤링을 할 때 time.sleep()을 하지 않을 경우 웹 페이지를 불러오기도 전에 다음 코드를 실행하여 오류가 발생하거나 크롤링하고자 하는 정보가 크롤링되지 않는 경우가 발생할 수도 있다. 
</br>또한 동적 웹크롤링 시에 반복적인 동작의 웹페이지 접속 등 비이상적인 행동이 감지될 경우 웹페이지에서 봇으로 감지하여 크롤링을 하지 못하도록 웹페이지에서 막을 수도 있다. 이를 방지하기 위해서는 특정 동작을 많이 반복해야 하는 경우, sleep 시간을 고정적으로 하는 것이 아닌 2~6 사이의 랜덤한 정수로 sleep하여 우회하는 방법이 존재한다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Web Crawling]]></title>
            <link>https://velog.io/@jyoung_710/Web-Crawling</link>
            <guid>https://velog.io/@jyoung_710/Web-Crawling</guid>
            <pubDate>Tue, 13 Aug 2024 19:22:17 GMT</pubDate>
            <description><![CDATA[<h1 id="span-stylebackground-colore6e6fa-web-crawling-vs-web-scraping-span"><span style="background-color:#E6E6FA"> Web Crawling vs Web Scraping </span></h1>
<hr>
<p>🪱 <span style="background-color:#D3E3F5; font-weight: bold;">웹 크롤링</span>은 말 그대로 웹을 기어다닌다는 의미로, 전체 웹사이트의 구조와 링크를 따라가며 데이터를 수집하는 것이다.
👓 <span style="background-color:#D3E3F5; font-weight: bold;">웹 스크래핑</span>은 크롤링과 달리 특정 웹페이지에서 필요한 정보만을 선택적으로 추출하는 것을 말한다. </p>
<hr>
<p>_<span style="color:gray;">웹 크롤링이 조금 더 대규모의 데이터를 수집한다는 점에서 차이가 있지만 두 용어를 혼용하여 많이 사용하는 듯 하다.</span>_</p>
<h1 id="span-stylebackground-colore6e6fa-web-crawling-processspan"><span style="background-color:#E6E6FA"> Web Crawling Process</span></h1>
<p>웹 크롤링의 절차는 크게 아래 세 과정을 거쳐 진행된다.</p>
<hr>
<ol>
<li>HTML 소스코드 불러오기</li>
<li>HTML 소스코드 파싱하기</li>
<li>원하는 정보 추출하기</li>
</ol>
<hr>
<h2 id="1-html-소스코드-불러오기">1. HTML 소스코드 불러오기</h2>
<p><img src="https://velog.velcdn.com/images/jyoung_710/post/bf9542c8-4a55-4fb3-98ad-020cb04eb54f/image.png" alt="웹페이지 불러오는 과정">
위 그림은 우리가 원하는 웹페이지를 불러와 받는 과정이다.</p>
<hr>
<p><code>https://www.naver.com</code>와 같은 도메인 주소를 <span style="background-color:#D3E3F5; font-weight: bold;">웹 서버에 요청(Request)</span>하면 웹 서버에서는 요청 받은 주소에 해당하는 웹 페이지 정보를 <span style="background-color:#D3E3F5; font-weight: bold;">사용자에게 전달(Response)</span>한다.</p>
<hr>
<p>우리가 얻고자 하는 HTML 소스코드도 웹 서버가 보낸 정보에 포함되어 있고, 이는 웹페이지에서 F12 혹은 오른쪽 클릭 후 &#39;페이지 소스 보기&#39; 클릭을 통해 쉽게 확인해볼 수 있다. 
<img src="https://velog.velcdn.com/images/jyoung_710/post/d35ff8a5-5cc5-49fc-a17b-df57a586bd07/image.png" alt="">
이 코드가 웹 서버로부터 받은 HTML 소스코드이고, 해당 코드를 바탕으로 우리가 웹페이지를 볼 수 있는 것이다. </p>
<p>이 HTML 소스코드를 파이썬으로 불러오기 위해 <strong>Requests</strong> 모듈을 사용할 것이다.</p>
<pre><code class="language-python">import requests

response = requests.get(&#39;https://www.naver.com/&#39;)</code></pre>
<p>requests.get(url)은 웹 서버에 url을 요청후 받은 서버의 응답 정보를 파이썬 객체로 받아온다. 해당 객체는 응답코드, HTML 소스코드, 인코딩 정보 등을 담고 있으며 아래와 같이 확인해볼 수 있다.</p>
<pre><code class="language-python">#응답코드
response.status_code

#HTML 소스코드
response.text

#인코딩 정보
resonse.encoding</code></pre>
<h2 id="2-html-소스코드-파싱하기">2. HTML 소스코드 파싱하기</h2>
<blockquote>
<p><span style = 'background-color:#ECE4E2; border-radius: 30%'>❓</span>  그런데,, 파싱이 뭔가요,,,?</p>
</blockquote>
<p>🎨<span style="background-color:#D3E3F5; font-weight: bold;">파싱(Parsing)</span>은 언어에서 구문을 분석하는 것을 말한다. 예를 들면 &quot;나는 햄버거를 먹는다.&quot;라는 문장에서 주어는 &quot;나는&quot;, 목적어는 &quot;햄버거를&quot;, 서술어는 &quot;먹는다&quot;라고 분석하는 것이다. 따라서 HTML에서 소스코드를 파싱한다는 것은 <span style="background-color:#D3E3F5; font-weight: bold;">HTML 소스코드의 문법을 분석한다</span>는 것과 같은 말이다. </p>
<p>이 파싱을 도와주는 라이브러리로 <strong>BeautifulSoup</strong>가 존재한다. </p>
<pre><code class="language-python">import requests
from bs4 import BeautifulSoup

html = requests.get(&quot;https://www.naver.com/&quot;).text
soup = BeautifulSoup(html, &#39;html.parser&#39;)</code></pre>
<p>위 코드는 requests를 통해 가져온 html 소스코드를 &#39;html.parser&#39;라는 파서(parser)로 파싱 후 BeautifulSoup 객체로 만들어 soup에 할당한 것이다. 즉 soup에는 해당 주소의 html 정보가 담긴 것이다. </p>
<h2 id="3-원하는-정보-추출하기">3. 원하는 정보 추출하기</h2>
<p>BeautifulSoup에서는 html을 파싱한 후 해당 정보를 바탕으로 정보를 추출할 수 있는 방법들을 제공하며, 다음은 BeautifulSoup에서 정보 추출에 가장 많이 쓰이는 것들이다.</p>
<blockquote>
<p>📌 <strong>.find(tag, attrs)</strong>
tag, attrs 조건에 맞는 첫 번째 태그 및 자식 태그를 모두 가져온다.</p>
</blockquote>
<pre><code class="language-python">#class가 &#39;news_tit&#39;인 첫 번째 ul 태그 및 자식 태그를 가져옴
soup.find(&#39;ul&#39;, attrs={&#39;class&#39;:&#39;news_tit&#39;})</code></pre>
<p>📌<strong>.find_all(tag, attrs)</strong>
tag, attrs 조건에 맞는 모든 태그를 가져와 리스트로 저장한다.</br>
📌<strong>.text</strong>
html 태그를 제거해준다.</p>
<pre><code>&lt;span class=&quot;NewsList_title__DgFuU&quot;&gt;[올림픽] 안세영 &quot;부상에 안일했던 대표팀 실망…계속 가기 힘들 수도&quot;(종합)&lt;/span&gt;</code></pre><p>위와 같은 태그가 news_title에 저장되었을 때 아래와 같이 작성하면</p>
<pre><code class="language-python">news_title.text</code></pre>
<pre><code>[올림픽] 안세영 &quot;부상에 안일했던 대표팀 실망…계속 가기 힘들 수도&quot;(종합)</code></pre><p>태그를 제외한 텍스트 정보만 추출된다.</br>
📌<strong>.get(key)</strong>
html 태그의 속성 정보를 가져올 수 있다.</p>
<pre><code class="language-python">news_title.get(&#39;class&#39;) #NewsList_title__DgFuU</code></pre>
<h1 id="span-stylebackground-colore6e6fa-practice-span"><span style="background-color:#E6E6FA"> Practice </span></h1>
<blockquote>
<p><span style = 'background-color:#ECE4E2; border-radius: 30%'>💡</span>  Yes24에서 도서 URL를 크롤링 해보자❕ </p>
</blockquote>
<p>우선 BeautifulSoup 클래스와 requests 모듈을 import하고, url에 대한 서버의 응답 정보를 response에 담는다.</p>
<pre><code class="language-python">from bs4 import BeautifulSoup  
import requests 

url = &quot;https://www.yes24.com//Mall/Main/Book/001?CategoryNumber=001&quot; 
response = requests.get(url)</code></pre>
<p>다음은 응답코드를 확인하여 200인지 확인하는 코드로, 해당 코드가 200일 때 크롤링을 진행하도록 한다.</p>
<blockquote>
<p><span style = 'font-weight:bold'>🔢응답코드(Status Code)</span>
<span style="background-color:#D3E3F5; font-weight: bold;">서버에서의 요청에 대한 처리 결과</span>를 세 자리의 숫자로 나타내주는데, 이 세 자리 숫자를 <span style="background-color:#D3E3F5; font-weight: bold;">응답코드(Status Code)</span>라고 한다.
  <table border = '1'>
    <tr>
      <th scope="col">코드</td>
      <th scope="col">의미</td>
    </tr>
    <tr>
      <td>1XX</td>
      <td> Informational → 정보제공</td>
    </tr>
    <tr>
      <td>2XX</td>
      <td> Success → 처리 성공</td>
    </tr>
  <tr>
      <td>3XX</td>
      <td> Redirection → 리다이렉션</td>
    </tr>
  <tr>
      <td>4XX</td>
      <td>  Client Error →클라이언트 에러</td>
    </tr>
  <tr>
      <td>5XX</td>
      <td> Server Error → 서버 에러</td>
    </tr>
  </table>
  <span style="color:gray;">( 번호 별 세부적인 의미를 갖고 있지만 특히 200번은 정보를 성공적으로 처리했음을 의미하는 코드이므로 여러모로 알아두면 쓸모있지 않을까 싶다. )</span></p>
</blockquote>
<pre><code class="language-python">if response.status_code == 200:
    print(&quot;웹 페이지를 성공적으로 가져왔습니다.&quot;)
else:
    print(&quot;웹 페이지를 가져오는 데 실패했습니다.&quot;)</code></pre>
<p>응답 내용(response)을 BeautifulSoup 객체로 파싱한다.</p>
<pre><code class="language-python">soup = BeautifulSoup(response.text, &#39;html.parser&#39;)</code></pre>
<p>HTML 소스코드에서 a태그의 링크들이 공통적으로 갖는 &#39;bgCateM&#39; 클래스를 찾아 해당 링크들을 스크래핑한다.</p>
<pre><code class="language-python">a_list = soup.find_all(&#39;a&#39;,&#39;bgCateM&#39;)
part_url_list = list(map(lambda x: x.get(&#39;href&#39;), a_list))
print(part_url_list)</code></pre>
<pre><code>[&#39;javascript:void(0);&#39;, &#39;javascript:void(0);&#39;, &#39;#&#39;, &#39;#&#39;, &#39;/Product/Goods/129366202&#39;, &#39;/Product/Goods/129094642&#39;, &#39;/Product/Goods/128902305&#39;, &#39;javascript:void(0)&#39;, &#39;/Product/Goods/128712025&#39;, &#39;/Product/Goods/129060701&#39;, &#39;/Product/Goods/129060449&#39;, &#39;/Product/Goods/129366517&#39;, &#39;/Product/Goods/126845471&#39;, &#39;/Product/Goods/115083494&#39;, &#39;/Product/Goods/125992096&#39;, &#39;/Product/Goods/63038113&#39;, &#39;/Product/Goods/129422847&#39;, &#39;/Product/Goods/129479023&#39;, &#39;/Product/Goods/129369207&#39;, &#39;/Product/Goods/67094041&#39;, &#39;javascript:void(0);&#39;, &#39;javascript:void(0);&#39;, &#39;javascript:void(0);&#39;, &#39;javascript:void(0);&#39;, &#39;javascript:void(0);&#39;, &#39;javascript:void(0);&#39;, &#39;/Product/Goods/129085141&#39;, &#39;/Product/Goods/128938074&#39;, &#39;/Product/Goods/128914849&#39;, &#39;/Product/Goods/128869653&#39;, &#39;/Product/Goods/129368125&#39;, &#39;/Product/Goods/129374769&#39;, &#39;/Product/Goods/129080531&#39;, &#39;/Product/Goods/129383425&#39;, &#39;javascript:void(0);&#39;, &#39;javascript:void(0);&#39;, &#39;javascript:void(0);&#39;, &#39;javascript:void(0);&#39;, &#39;/Product/Goods/128849823&#39;, &#39;/Product/Goods/127261797&#39;, &#39;/Product/Goods/128850058&#39;, &#39;/Product/Goods/129368189&#39;, &#39;/Product/Goods/124999476&#39;, &#39;/Product/Goods/127766405&#39;, &#39;/Product/Goods/128120256&#39;, &#39;/Product/Goods/127067698&#39;, &#39;javascript:void(0);&#39;, &#39;javascript:void(0);&#39;, &#39;javascript:void(0);&#39;, &#39;javascript:void(0);&#39;, &#39;/Product/Goods/129063553&#39;, &#39;/Product/Goods/129120227&#39;, &#39;/Product/Goods/129392822&#39;, &#39;/Product/Goods/129775270&#39;, &#39;/Product/Goods/129060654&#39;, &#39;/Product/Goods/128818096&#39;, &#39;/Product/Goods/129391394&#39;, &#39;/Product/Goods/129144880&#39;, &#39;javascript:void(0);&#39;, &#39;javascript:void(0);&#39;, &#39;javascript:void(0);&#39;, &#39;javascript:void(0);&#39;, &#39;/Product/Goods/129123348&#39;, &#39;/Product/Goods/129375440&#39;, &#39;/Product/Goods/129392568&#39;, &#39;/Product/Goods/129479023&#39;, &#39;/Product/Goods/129365542&#39;, &#39;/Product/Goods/129109015&#39;, &#39;/Product/Goods/129132445&#39;, &#39;/Product/Goods/129053063&#39;, &#39;javascript:void(0);&#39;, &#39;javascript:void(0);&#39;, &#39;javascript:void(0);&#39;, &#39;javascript:void(0);&#39;, &#39;https://www.yes24.com/product/goods/128724816&#39;, &#39;https://www.yes24.com/product/goods/125131585&#39;, &#39;https://www.yes24.com/product/goods/126011798&#39;, &#39;https://www.yes24.com/product/goods/125075376&#39;, &#39;https://www.yes24.com/product/goods/128071226&#39;, &#39;https://www.yes24.com/product/goods/128548452&#39;, &#39;https://www.yes24.com/product/goods/128193282&#39;, &#39;http://www.yes24.com/24/goods/128860445&#39;, &#39;https://www.yes24.com/product/goods/118204268&#39;, &#39;https://www.yes24.com/product/goods/116604749&#39;, &#39;https://www.yes24.com/product/goods/125830202&#39;, &#39;https://www.yes24.com/product/goods/123054441&#39;, &#39;javascript:void(0);&#39;, &#39;javascript:void(0);&#39;]</code></pre><blockquote>
</blockquote>
<p><span style = 'font-weight:bold'>🕹️lambda 함수</span>
람다 함수는 다음과 같은 형태로 정의한다.
<span style='padding: 0.25rem;
  background-color: #F1F1F1;
  border-radius: 5px;
  font-family: "Consolas", "Sans Mono", "Courier", "monospace";
  '>lambda 인자: 표현식</span><br>
  람다 함수는 함수를 기존의 def보다 <span style="background-color:#D3E3F5">간단하게 정의하고 사용</span>하기 위해 고안되었다. 
따라서 아래의 def 함수는 람다 함수로 표현할 때 더 간단하게 표현할 수 있다.</p>
<pre><code class="language-python"> def sum(x,y):  
     return x+y</code></pre>
<pre><code class="language-python"> sum = lambda x,y: x+y</code></pre>
<p>   이때 sum은 람다 함수를 참조하게 된다.</p>
<p>위 코드의 출력을 보면 <code>javascript:void(0);</code>, <code>#</code>, <code>/Product/Goods/129366517</code> 등 다양한 형태로 수집된 것을 볼 수 있다. 이들 중 <code>/Product/Goods/129366517</code>가 URL에서 보이는 경로와 유사하게 생긴 것을 알 수 있고, 실제로 <code>https://www.yes24.com//Product/Goods/129366517</code> 경로로 이동하면 도서 구매 페이지로 이동한다. 
따라서 <code>part_url_list</code> 요소들 중 <code>Product/Goods</code>를 포함하는 요소들만 따로 추출하기로 한다.</p>
<pre><code class="language-python">url_list = []
full_url = &#39;https://www.yes24.com&#39;
for part_url in part_url_list:
    if &#39;/Product/Goods&#39; in part_url:
        url = full_url + part_url
        url_list.append(url)</code></pre>
<p><code>url_list</code>에는 도서 구매 페이지로 이동하는 전체 경로가 담기게 된다. </p>
]]></description>
        </item>
    </channel>
</rss>