<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>hoho_u.log</title>
        <link>https://velog.io/</link>
        <description>인생 1회차 - 이 정도면 잘하고이따</description>
        <lastBuildDate>Thu, 15 Jun 2023 06:22:33 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>hoho_u.log</title>
            <url>https://images.velog.io/images/hoho_dev/profile/ef6886e3-1880-4f05-9de0-97dc5fdb8224/9934DA4A5B8E56142D.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. hoho_u.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/hoho_dev" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[ArcFace: Additive Angular Margin Loss for Deep Face Recognition]]></title>
            <link>https://velog.io/@hoho_dev/ArcFace-Additive-Angular-Margin-Loss-for-Deep-Face-Recognition</link>
            <guid>https://velog.io/@hoho_dev/ArcFace-Additive-Angular-Margin-Loss-for-Deep-Face-Recognition</guid>
            <pubDate>Thu, 15 Jun 2023 06:22:33 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가기-전">들어가기 전</h1>
<p>위 논문을 읽게 된 계기는 회사에서 Recognition 관련해서 새롭게 업무를 진행해야했기 때문이다.
정확히 Face recognition은 아니고, Face recognition task 처럼 학습에 사용한 클래스(얼굴 인식에서는 특정 사람)는 Gallery에 등록하고, 학습하지 않은 클래스(얼굴 인식에서는 새롭게 등장한 사람)는 등록되지 않았는지 확인하기 위해서 Face recognition 개념을 도입했다.(정확히는 Face identification)</p>
<h1 id="introduction">Introduction</h1>
<ul>
<li><p>해당 논문에서는 <strong>Additive Angular Margin</strong> 이라는 새로운 Loss 함수를 소개하고 있다.</p>
</li>
<li><p><strong>Additive Angular Margin Loss</strong>는 Face recognition 모델의 discriminative power를 향상시킨다.</p>
</li>
<li><p><strong>Backbone(ConvNet)의 output인 feature vector와 마지막 FC Layer의 weight 사이의 내적 연산은 feature vector와 각 class를 대표하는 center vector 간의 cosine distance를 계산하는 것으로 보았음(이 개념을 아는 것이 논문 및 코드를 이해하는데 아주 중요함!)</strong></p>
<ul>
<li>각 Class에 해당하는 feature vector가 각 Class의 center vector와 cosine distance가 가깝도록 학습이 되는 것</li>
<li>내적하기 전에 feature vector와 fc layer의 weight(center vector) 에 대해서 normalization을 수행해주는데, 이를 통해 feature vector가 구성하는 manifold가 hypersphere(원/구) 형태로 나오게 되는데, 이 때문에 ArcFace 라고 부르기도 함</li>
</ul>
</li>
</ul>
<ul>
<li>Training Data 구성이 Clean 한 것도 있지만, labeling이 잘못되어 있거나 이미지가 noisy 한 경우도 있는데, 이를 <strong>Sub-Center ArcFace</strong> 라는 개념을 도입해 모델의 robustness 강화시킴<ul>
<li>feature vector가 하나의 positive center에 가깝도록 하는 것이 아니라, k개의 sub-center에 가깝게 학습되도록 함<ul>
<li>One positive center에 가깝도록 학습하는 것 보다는 constraint를 완화시켜주는 역할을 함</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1 id="proposed-approach">Proposed Approach</h1>
<h2 id="arcface">ArcFace</h2>
<p><img src="https://velog.velcdn.com/images/hoho_dev/post/b2aa15c0-f4cc-4911-8c9d-535fb70bc424/image.png" alt=""></p>
<ul>
<li>Classification model을 학습할 때 주로 사용하는 Softmax loss는 아래와 같음
<img src="https://velog.velcdn.com/images/hoho_dev/post/4e7f9e98-d61b-4f68-8717-6efeb944e266/image.png" alt=""></li>
</ul>
<ul>
<li><p>여기서 $x_i$는 $i$-th sample에 대한 feature vector($y_i$-th class)</p>
</li>
<li><p>$W_{y_i}$는 $y_i$에 해당하는 weight vector로서, 위에 언급한 각 class에 해당하는 center vector를 의미함</p>
</li>
<li><p>${W_{y_i}}^T$$x_i$는 logit이라고 함</p>
</li>
<li><p>위와 같은 loss를 했을 때 전반적으로 좋은 성능을 보이지만, intra-class(동일한 class) 샘플에 대해서는 높은 유사도, inter-class(서로 다른 class) 샘플에 대해서는 분리되도록 <strong>명시적(explicitly)</strong>으로 학습하는 구조가 아니라서 분류 성능에 한계가 있음</p>
</li>
<li><p>위 식에서 $b_j$ 및 $b_{y_i}$를 0으로 두면 logit은 아래와 같이 쓸 수 있음</p>
<ul>
<li>${W_j}^Tx_i=||W_j|| \ ||x_i|| \ cos{\theta}_j$<ul>
<li>$cos{\theta}_j$는 weight ${W_j}$와 feature $x_i$ 간의 angle을 의미함</li>
</ul>
</li>
</ul>
</li>
<li><p>각각의 $W_j$를 $l_2$ normalization 을 통해 $||W_j||=1$ 로 만듦</p>
</li>
<li><p>$||x_i||$ 또한 $l_2$ normalization 하고, $s$로 re-scale 하면 아래와 같은 새로운 식이 됨</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hoho_dev/post/f14a6eed-4a2d-450b-9ecf-296ea25b504e/image.png" alt=""></p>
<ul>
<li><p>feature vector는 주로 center feature vector 주위에 분포되어 있음</p>
</li>
<li><p>discriminative power를 향상 시키기 위해서 <strong>intra-class에 대해서는 compactness, inter-class에 대해서는 discrepancy</strong>를 높여야 함</p>
</li>
<li><p>이를 위해 a<strong>dditive angular margin penalty $m$</strong>을 부여함</p>
</li>
<li><p>최종적으로는 아래와 같은 <strong>Additive angular margin loss</strong>가 도출됨</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hoho_dev/post/297e2b94-af16-41c7-a891-9741afe798d5/image.png" alt=""></p>
<ul>
<li><p>$\theta$에 따른 decision boundary를 그려보게 되면 아래와 같이 표현할 수 있다.
<img src="https://velog.velcdn.com/images/hoho_dev/post/6655b63b-611b-4b6e-9da2-09566a91fb4e/image.png" alt=""></p>
<ul>
<li><p>Softmax: margin이 없으므로 Class1, 2에 대한 decision boundary가 딱 붙어있다.</p>
</li>
<li><p>나머지 margin-based loss: margin가 있기 때문에 Class1, 2에 대한 decision boundary 가 다소 떨어져있음을 확인할 수 있다.</p>
<ul>
<li>SphereFace는 각도가 작을수록(0에 가까울수록) decision boundary가 작아지는 단점이 있다</li>
<li>CosFace는 decision boundary가 linear 하지 않음</li>
<li>ArcFace는 각도 구분 상관없이 linear 한 decision boundary를 가진다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="code">Code</h2>
<p>논문은 그렇게 어려운 내용이 아니다. 동일한 class 끼리는 각도가 최소화 되도록, 다른 class끼리는 각도가 최대화 되도록 학습하는 것이고, margin을 주어 intra-class의 compactness, inter-class의 discrepancy를 강화하겠다는 것이 이 논문 내용의 전부다. 항상 그렇듯, 코드는 논문의 내용과 100% 동일하지 않다. 코드는 arcface-pytorch(<a href="https://github.com/ronghuaiyang/arcface-pytorch)%EB%A5%BC">https://github.com/ronghuaiyang/arcface-pytorch)를</a> 참고한다. 전체 코드는 아래와 같다.</p>
<pre><code class="language-python">class ArcMarginProduct(nn.Module):
    r&quot;&quot;&quot;Implement of large margin arc distance: :
        Args:
            in_features: size of each input sample
            out_features: size of each output sample
            s: norm of input feature
            m: margin

            cos(theta + m)
        &quot;&quot;&quot;
    def __init__(self, in_features, out_features, s=30.0, m=0.50, easy_margin=False):
        super(ArcMarginProduct, self).__init__()
        self.in_features = in_features
        self.out_features = out_features
        self.s = s
        self.m = m
        self.weight = Parameter(torch.FloatTensor(out_features, in_features))
        nn.init.xavier_uniform_(self.weight)

        self.easy_margin = easy_margin
        self.cos_m = math.cos(m)
        self.sin_m = math.sin(m)
        self.th = math.cos(math.pi - m)
        self.mm = math.sin(math.pi - m) * m

    def forward(self, input, label):
        # --------------------------- cos(theta) &amp; phi(theta) ---------------------------
        cosine = F.linear(F.normalize(input), F.normalize(self.weight))
        sine = torch.sqrt((1.0 - torch.pow(cosine, 2)).clamp(0, 1))
        phi = cosine * self.cos_m - sine * self.sin_m
        if self.easy_margin:
            phi = torch.where(cosine &gt; 0, phi, cosine)
        else:
            phi = torch.where(cosine &gt; self.th, phi, cosine - self.mm)
        # --------------------------- convert label to one-hot ---------------------------
        # one_hot = torch.zeros(cosine.size(), requires_grad=True, device=&#39;cuda&#39;)
        one_hot = torch.zeros(cosine.size(), device=&#39;cuda&#39;)
        one_hot.scatter_(1, label.view(-1, 1).long(), 1)
        # -------------torch.where(out_i = {x_i if condition_i else y_i) -------------
        output = (one_hot * phi) + ((1.0 - one_hot) * cosine)  # you can use torch.where if your torch.__version__ is 0.4
        output *= self.s
        # print(output)

        return output
</code></pre>
<p>step-by-step으로 이해해보자.</p>
<h3 id="forward">forward</h3>
<pre><code class="language-python">        cosine = F.linear(F.normalize(input), F.normalize(self.weight))
        sine = torch.sqrt((1.0 - torch.pow(cosine, 2)).clamp(0, 1))
        phi = cosine * self.cos_m - sine * self.sin_m
        if self.easy_margin:
            phi = torch.where(cosine &gt; 0, phi, cosine)
        else:
            phi = torch.where(cosine &gt; self.th, phi, cosine - self.mm)
</code></pre>
<ul>
<li><p>cosine: input(feature vector)와 각 class에 대한 center vector의 모음인 weight를 내적한 결과이다.</p>
<ul>
<li>만약, class 개수가 75개라고 하면, 내적 결과는 1x75가 되는데, 이는 각 class weight와 feature vector 사이의 cosine distance를 의미한다.</li>
</ul>
</li>
<li><p>phi: 단순하게 $cos(\theta +m)$이라고 생각하면 된다.</p>
<ul>
<li>코사인 덧셈법칙: $cos(x+y)=cos(x)<em>sin(y)+sin(x)</em>cos(y)$</li>
</ul>
</li>
</ul>
<h4 id="easy-margin">easy margin</h4>
<pre><code class="language-python">    if self.easy_margin:
            phi = torch.where(cosine &gt; 0, phi, cosine)
        else:
            phi = torch.where(cosine &gt; self.th, phi, cosine - self.mm)</code></pre>
<p>주로 easy margin을 사용하지 않으므로 else인 부분에 대해서 설명하겠다. case는 아래와 같다.</p>
<ul>
<li>$cos(\theta)$ &gt; $cos(\pi-m)$일 때<ul>
<li>$cos(\theta+m)$</li>
</ul>
</li>
<li>$cos(\theta)$ &lt;= $cos(\pi-m)$일 떄, <ul>
<li>$cos(\theta)$ - $m*sin(\pi-m)$</li>
</ul>
</li>
</ul>
<p>이렇게 수식이 복잡한 이유는, $\theta+m$ 값이 [0,$\pi$]에 있지 않아 $cos$가 단조감소 함수가 아닐 때가 있는데, 범위를 벗어나더라도 단조감소 함수로 만들기 위함이다.</p>
<ul>
<li>같은 class 끼리는 $\theta$가 작아져야함($W_j$와 $x_i$가 같은 class 라면 $\theta$는 작아져야함): $\theta$가 0에 수렴하면 $cos(\theta)$는 커짐</li>
<li>다른 class 끼리는 $\theta$가 커져야함($W_j$와 $x_i$가 다른 class 라면 $\theta$는 작아져야함): $\theta$가 $\pi$에 가까워지면 $cos(\theta)$는 작아짐</li>
</ul>
<p>즉, 1번 case는 $\theta+m$ 값이 $[0, \pi]$ 일 때 즉, 단조감소함수 조건을 만족할 때는 $cos(\theta+m)$을 그대로 사용하겠다는 말이다.($\theta+m$ &lt;= $\pi$ &lt;-&gt; $\theta$ &lt;= $\pi-m$, 즉, $cos(\theta)$ &lt;= $cos(\pi-m)$)  </p>
<p>2번 case는 $\theta+m$ 값이 $[0, \pi$] 가 아닐 때를 의미하는 것이다. 이 때, taylor series로 근사하여 단조감소함수를 만들겠다는 의미이다.</p>
<p>사실, $\theta+m$ 값이 $[0, \pi$]를 벗어났을 때 제일 쉬운 방법은 $cos(\theta+m)=-1$로 표현하는 방법이지만, 정확도가 떨어진다.</p>
<p>다른 방법은, 위에서 얘기한 것처럼 taylor series를 이용하여 근사하는 방법이 있다.(위 코드에서는 taylor series에서 1차 선형식까지만 이용하여 근사한다. 물론, 더 근사하면 정확도는 높아지겠지만, 크게 차이가 없는데 computational cost만 높이니까 1차만 사용한 게 아닐까?)</p>
<p><img src="https://velog.velcdn.com/images/hoho_dev/post/380cb755-0594-4ebc-8375-ef8534cd4115/image.png" alt=""></p>
<ul>
<li>위 근사식에 따라, $cos(\theta+m)$ $\sim$ $cos(\theta)$ $-m*sin(\theta)$</li>
<li>$\theta+m$ &gt;= $\pi$ 즉, $\theta$ &gt;= $\pi-m$ 인 경우에는 $sin(\theta)&gt;=sin(\pi-m)$ 이므로, 최종적으로 아래식으로 근사된다.</li>
<li>$cos(\theta+m)$ $\sim$ $cos(\theta)$ $-msin(\pi-m)$</li>
</ul>
<h3 id="logit">logit</h3>
<pre><code class="language-python">        # one_hot = torch.zeros(cosine.size(), requires_grad=True, device=&#39;cuda&#39;)
        one_hot = torch.zeros(cosine.size(), device=&#39;cuda&#39;)
        one_hot.scatter_(1, label.view(-1, 1).long(), 1)
        # -------------torch.where(out_i = {x_i if condition_i else y_i) -------------
        output = (one_hot * phi) + ((1.0 - one_hot) * cosine)  # you can use torch.where if your torch.__version__ is 0.4
        output *= self.s
        # print(output)</code></pre>
<ul>
<li>이 부분은, target class만 $cos(\theta+m)$만 하기 위함이며, target이 아닌 나머지 class에 대해서는 $cos(\theta)$로 사용한다.</li>
<li>그리고 scale 값인 s를 곱해줘서 최종적으로 output을 만들며, 이후 cross-entropy loss를 통해 학습하게 된다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[2021년 회고 - 1]]></title>
            <link>https://velog.io/@hoho_dev/2021%EB%85%84-%ED%9A%8C%EA%B3%A0-1</link>
            <guid>https://velog.io/@hoho_dev/2021%EB%85%84-%ED%9A%8C%EA%B3%A0-1</guid>
            <pubDate>Fri, 31 Dec 2021 13:26:10 GMT</pubDate>
            <description><![CDATA[<p>2021년에 있었던 일들을 주제별로 여러 차례에 글로 정리해본다. 
글 쓰다보면 글 쓰는 능력도 좋아지겠지라는 매우 심플한 생각으로 앞으로 글을 자주 써보려 한다.</p>
<h3 id="1-취뽀">1. 취뽀</h3>
<p>2020년 9월에야 시작된 취업준비, 사실 조금 늦은 감이 없지않아 있었다. 
애초에 대학원 진학을 목표로 하고 있어서 취업 준비를 크게 하지 않았을뿐더러, 중소/스타트업/대기업 인턴 3번의 경험과 4.1점 정도 되는 학점을 가지고 있었기에 서류는 어디든 붙을거라 생각했다. 면접도 나름의 여러 경험이 있어서 자신감이 있었다.</p>
<p>대학원 진학을 포기하고 급하게 취업으로 방향을 돌리면서 준비할틈 없이 벼락치기로 대기업에 지원할 최소한의 스피킹 점수만 만들었다. 
많은 사람들은 방학 전부터 자소서를 준비해놓고 서류 시즌이 되면 인적성을 준비하는데, 나는 서류 시즌이 되어서야 때맞춰 자소서를 작성하는 등 전반적으로 형편없는 준비 과정을 거쳤다. 
모든 게 찜찜했고 부족하다는 생각이 들었다. 자기 확신에 대한 문제가 아니었다. 정성과 시간 투자의 <strong>문제</strong>가 있다고 생각이 들었다.</p>
<p>역시는 역시였을까, 결과는 참담했다. 이어지는 서류/면접 탈락에 정신을 차리지 못했다. 하지만 너무나 당연한 결과였다.
방향도 없었고, 내가 이루고자 하는 바가 무엇인지도 몰랐기에 앞으로 나아가지 못하는 게 자연스러웠다.</p>
<p>그렇게 첫 취업 준비에 제대로 실패하고 2021년 상반기를 노려야겠다는 생각을 하고있던 시점에, 친한 형으로부터 본인이 다니고 있는 회사에 나를 추천을 해주겠다고, 한번 지원해보지 않겠냐고 제의를 받았다.
학교에서 그 회사에 대해 많은 얘기를 들어보긴 했지만, 한번도 생각해보지 않은 회사였고 취업을 실패한 주제에 눈은 높아서 성에 차지 않았다. </p>
<p>하지만, 불안정한 상태에서 취업을 준비하느니 안정된 상태에서 취업을 준비해도 되겠다는 생각이 들어서 기본적인 서류와 포트폴리오를 작성해서 제출했고 서류 합격이 되어 과제 전형에 들어가게 되었다.
막상 과제 전형에 들어가게 되니까, 크게 기대하지 않았음에도 불구하고 취업이라는 커다란 벽을 넘고자 열심으로 전형에 임했던 것 같다. 어쩌다보니 과제 전형 결과 리포트로 42장(?)을 제출하게 되었다. 평가하는 사람은 굉장히 싫었을 것 같다. 그러나 어쩌겠는가. 학부 시절부터 이렇게 해서 좋은 성적을 얻었으며 애초에 대충대충 이런거는 내 성격과 안맞는다.
<img src="https://images.velog.io/images/hoho_dev/post/93c110ce-1a1b-4af1-88e3-b9371b703c74/image.png" alt=""></p>
<p>42장을 써서 그랬던 탓일까, 과제 전형은 가뿐히 패스했고 인터뷰를 제안받았다.
<img src="https://images.velog.io/images/hoho_dev/post/8c3372ee-4917-41ac-b226-27b02693d279/image.png" alt=""></p>
<p>2~3일 정도 인터뷰를 준비할 시간이 있었고, 인성 관련 질문들은 주로 나올만한 것들을 추려서 그에 대한 답변들을 나름대로 정리했다. 그리고 코딩테스트 관련해서는 어떻게 이 문제에 대해 접근했고 어떤 과정을 거쳐서 풀었으며 더 좋은 방법들을 나름대로 정리해서 인터뷰를 준비했다.</p>
<p>인터뷰는 비대면으로 진행되었고, 1차는 인사담당자 &amp; CEO님과의 Culture fit/인성 면접, 그리고 2차는 R&amp;D 소장님과의 Tech 면접으로 진행되었다.
1차 인터뷰 시작하기 전에, 인사담당자님께서 과제 전형을 너무 잘보셨다며 칭찬을 해주셨는데, 이때부터 자신감이 차올라서 면접에 적극적으로 임할 수 있었고 질문에 대한 답변도 무리없이 할 수 있었다.</p>
<p>2차 인터뷰는 코딩테스트 및 기술 관련된 면접이었고, 연구 소장님께서도 과제 전형을 너무 잘했다고 칭찬해주셔서 좋은 분위기에서 인터뷰를 볼 수 있었던 것 같다.
기본적인 C++ 관련 질문들과 코딩테스트 문제들 관련 질문 위주로 이루어졌다. 과제 전형에 적극적으로 참여했기 때문에 내가 푼 문제에 대해서 크게 무리없이 답변할 수 있었다.
그리고 언제쯤 출근할 수 있냐는 질문을 받았는데, 이때 &#39;아.. 붙었구나..&#39; 라는 확신을 가졌던 것 같다. ㅎㅎ 그리고 화기애애한 분위기에서 인터뷰가 마무리 되었다.</p>
<p>사담으로, 인터뷰 제의 메일에 참조로 되어있는 분이 여러분 계셨는데 그중에 인사담당자 이실 것 같은 분을 구글링을 통해서 찾아서 관련 정보들을 사전에 research(?)를 했다ㅋㅋㅋ 
채용 또한 사람이 하는 것이기 때문에 관련된 분들에 대한 정보를 아는 것이 인터뷰 과정에 도움이 될 거라고 생각했다. 구글링 및 주변 지인들을 통해 그분이 어떤 성향을 가지고 계신분인지, 어떤 생각을 가지고 계신지를 알 수 있었다.
놀랍게도, 실제로 인사담당자께서 1차 인터뷰에 참석하셨고 사전에 조사했던 정보들이 인터뷰 과정에서 답변을 하는데 있어 큰 도움이 되었다.</p>
<p>결과는 1주일 뒤에 나왔고, <strong>최종합격</strong> 이라는 문자/메일과 함께 처우 제안을 받았다.
<img src="https://images.velog.io/images/hoho_dev/post/f4b50c00-6ea1-44d0-b221-6aaf24d519f9/image.png" alt=""></p>
<p>그렇게 어떻게 하다보니 취업을 하게 되었고 본격적인 자율주행 딥러닝/알고리즘 개발자의 길로 들어서게 되었다. 험난한 여정의 시작이었던 것을 이제서야 깨달았다.</p>
]]></description>
        </item>
    </channel>
</rss>