Gitignore 파일 생성 및 이용

|

Gitignore 파일 생성 및 이용

  • Git으로 프로젝트 관리 시 특정 파일들은 git으로 관리 할 필요가 없는 경우에 사용
    • 자동으로 생성되는 파일 및 weight 파일 등 용량이 큰 파일에 대하여 적용 가능

Gitignore 파일 만들기

  • touch .gitignore 로 파일 생성
    • 맨 앞에 “.” 이 붙어있어야 함
  • vim .gitignore로 제외시키고 싶은 파일 또는 폴더, 확장자를 넣어 저장한다.
    • ex 1. weight parameters가 저장된 용량이 큰 trained_nets 폴더를 제외시키고 싶다면
      • trained_nets 적은 후 wq로 저장
    • ex 2. *.log 파일들을 제외시키고 싶다면
      • *.log 적은 후 wq로 저장
    • 이렇듯 제외 시키고 싶은 파일 또는 폴더, 확장자명을 추가할 수 있다.
  • 다음을 git add . 로 모든 파일을 커밋 준비상태로 만든 후 git push로 커밋하게 되면 해당 파일들이 제외된 것을 확인 할 수 있다.

Github에 프로젝트 쉽게 올리기

|

Github에 프로젝트 쉽게 올리기

  • 업로드 할 폴더로 이동 후 git init 입력
    • .git이라는 파일이 생성됨
  • .gitignore 파일을 만들어서 업로드하여 버전 관리를 하지 않을 파일을 설정
  • git add .으로 해당 프로젝트 폴더 내의 모든 파일을 버전 관리하도록 추가
    • .gitignore 파일에서 제외시키는 파일은 자동으로 버전관리에 추가되지 않음
  • git status로 버전 관리하도록 tracking이 잘 되었는지 확인 가능
    • 초록 글씨로 new file: filename 로 표시되어있음
  • git commit -m "commit_message" 으로 커밋
  • github.com 으로 접속하여 로그인 하고, 해당 파일들을 push 해 줄 github 저장소 생성
    • 로그인 후 우측 상단에 “+” 버튼을 누른 후 “New repository” 선택
    • Repository name과 Description 입력(추후 수정 가능)
    • 다음으로 “Create repository” 버튼을 누른 후 https github 주소 복사
      • ex. https://github.com/seongkyun/pytorch-classifications.git
  • 터미널에서 git remote add repository_name repository_address 입력
    • ex. git remote add origin https://github.com/seongkyun/pytorch-classifications.git
    • repository_name은 편리하게 설정하면 되나 간편하게 “origin” 으로 설정
  • git push origin master 로 push
    • github의 계정 정보를 기입하여 로그인 하면 자동으로 push 진행됨
  • github 웹에서 업로드된 파일을 변경하게 되면 지역저장소에서 수정 전에 폴더 내에서 git pull로 최신 버전(master)으로 동기화 후 작업해야함!
    • 충돌이 발생해서 해결하려면 귀찮아진다..
  • 지역저장소에서 작업 후 push 시에는 git push origin master 를 치면 된다.

Github에 수정된 파일 동기화(업로드) 시키기

  • 작업 전 git pull <remote> <branch> 로 동기화
    • ex. git pull origin master
  • 작업한 내용을 모두 저장 한 후 git add .로 git의 stage에 올려줌
  • 다음으로 git commit -m "commit message" 로 커밋
  • 커밋 후 git push origin master로 push
    • 로그인 정보 치면 push 완료

  • [참고글]

https://medium.com/wasd/github%EC%97%90-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%89%BD%EA%B2%8C-%EC%98%AC%EB%A6%AC%EA%B8%B0-django-1e2c7814a13

CH6. 스택 (Stack) 3

|

CH6. 스택 (Stack) 3

6-4 계산기 프로그램 구현

문제 6-2

  • 중위 표기법의 후위 표기법으로의 변환
    • 문제 1: 3 + 2 * 4
    • 문제 2: 2 * 4 + 3
    • 문제 3: 2 * 1 + 3 / 2
  • 문제 1 풀이
    • 1: * 4
      • Stack: + <-top
      • 변환수식: 3, 2
    • 2: emtpy
      • Stack: +, * <-top
      • 변환수식: 3, 2, 4
    • 최종 답: 3, 2, 4, *, +
  • 문제 3 풀이
    • 1: 2 * 1 + 3 / 2
      • Stack: empty
      • 변환수식: empty
    • 2: + 3 / 2
      • Stack: * <-top
      • 변환수식: 2, 1
    • 3: / 2
      • Stack: + <-top
      • 변환수식: 2, 1, *, 3
    • 4: empty
      • Stack: +, / <-top
      • 변환수식: 2, 1, *, 3, 2
    • 최종 답: 2, 1, *, 3, 2, /, +

문제 6-3

  • 중위 표기법의 후위 표기법으로의 변환
    • 문제 : (1 * 2 + 3) / 4
  • 문제 풀이
    • 1: (1 * 2 + 3) / 4
      • Stack: (, * <-top
      • 변환수식: 1, 2
    • 2: ) / 4
      • Stack: (, + <-top
      • 변환수식: 1, 2, *, 3
    • 3: empty
      • Stack: empty
      • 변환수식: 1, 2, *, 3, +, 4, /
    • 최종 답: 1, 2, *, 3, +, 4, /

중위 -> 후위: 프로그램 구현

views
  • 중위 표기법으로 입력받은 수식을 후위 표기법으로 변환한다.
views
  • 중위 표기법을 후위 표기법으로 변환하기 위해서는 두 개의 Helper function을 사용한다
    • 연산자의 우선 순위를 숫자 형태로 반환하는 GetOpPrec 함수
    • 두 연산자간에 비교 우위를 따져주는 WhoPrecOp 함수
  • GetOpPrec 함수는 우선순위가 높을수록 큰 값을 반환한다.
  • ”(“의 경우 연산자 Stack에서 괄호의 시작을 알리는 역할을 하므로 우선순위가 가장 낮게 설정되어야 한다.
    • 괄호의 시작은 곧 Stack에서의 새로운 바닥을 의미하므로 제일 밑에 존재해야 한다.
    • 따라서 +, - 연산자보다 더 낮은 중요도를 갖고있어야 한다.
  • 정해진 연산자 외의 경우 처리를 위하여 마지막에 -1을 반환한다.
views
  • WhoPrecOP 함수는 두 연산자 op1과 op2를 입력 받고, 우선순위를 비교한다.
  • GetOpPrec 함수로 연산자를 넘겨 우선순위를 반환받아 각각 op1Prec와 op2Prec로 저장한다.
  • 숫자가 클수록 연산의 우선순위가 높으므로 op1의 우선순위가 높다면 1, 아니라면 -1, 같다면 0을 반환한다.
    • 우선순위가 같은 경우에 대해서도 먼저 연산자 Stack에 존재하는 연산자가 우선순위가 높으므로 별도 처리가 필요하다.
views
  • ConvToRPNExp 함수를 통해 후위표기법으로 변환해준다.
    • 앞의 WhoPrecOPGetOpPrec 함수를 이용
  • 연산자를 쌓을 연산자 스택인 stack을 정의하고, 변환된 수식이 저장될 convExp를 정의
    • convExp는 str형이므로 마지막 ‘\n’이 저장되어야 하므로 (char*)malloc(expLen+1)로 초기화됨
  • 다음으로 memset을 통해 convExp의 모든 값을 0으로 초기화
  • StackInit으로 연산자stack의 초기화
    • Stack의 top이 NULL을 가리키도록 함
  • for 문 안에서 중위표기법을 후위표기법으로 변환하도록 함
    • 주요 연산과정!
  • while문에서는 스택이 비어있는지 판단하고, 스택이 비어있지 않다면 연산자Stack 내부의 모든 남아있는 연산자들을 변환된 수식인 convExp에 붙임
  • 마지막으로 변환된 수식을 원래 수식인 exp으로 복사하고 convExp를 삭제
views
  • 메인 연산인 for문에서는 해당 인덱스 번째 문자를 tok으로 받고, 그게 숫자인지 아닌지에 따라 연산을 구분
  • tok 문자가 숫자인 경우(isdigit(tok)이 1을 반환)
    • 변환수식인 convExp에 tok을 가져다 붙임
  • tok 문자가 문자(연산자)인 경우
    • Switch 문을 이용하여 올바른 순서대로 연산자Stack에 쌓음
views
  • tok이 “(“이라면 괄호의 시작으로 연산자Stack에 새로운 바닥을 깔아야 함
    • 바닥 역할을 하도록 “(“을 연산자Stack에 넣고 다음 tok으로 넘어감
  • tok이 “)”이라면 괄호의 끝을 의미하므로 연산자Stack에서 차례대로 연산자를 꺼내 연산자Stack의 top이 (popOp)”(“일때까지 convExp에 붙임
    • 연산자Stack에는 아래의 tok이 연산자인 경우일 때 올바른 순서대로(우선순위) 연산자가 쌓여있음
    • ”(“이 나온다면 바닥을 의미하므로 break로 빠져나오고 다음 tok으로 넘어감
  • tok이 연산자라면(+, -, *, /)
    • 연산자Stack이 비어있지 않고(!SIsEmpty(&stack)) 연산자Stack의 top 연산자가 tok 연산자보다 우선순위가 높은 동안(WhoPrecOp(SPeek(&stack), tok)>=0))
      • 이 과정을 통해 연산자Stack의 top값과 tok의 우선순위 비교를 수행한다.
      • WhoPrecOp(SPeek(&stack), tok)에서, 연산자Stack의 top의 중요도가 tok보다 중요하다면 while 문 실행 (WhoPrecOp(SPeek(&stack), tok) > 0)
        • tok은 연산자Stack에 쌓일 수 없다.
        • 따라서 연산자Stack의 top 값을 convExp에 가져다 붙인다. (convExp[idx++] = SPop(&stack))
      • WhoPrecOp(SPeek(&stack), tok)에서, 연산자Stack의 top의 중요도가 tok과 같다면 while 문 실행 (WhoPrecOp(SPeek(&stack), tok) = 0)
        • tok은 먼저 연산자Stack에 존재하던 연산자를 빼내고 자신이 쌓여야 한다.
        • 따라서 연산자Stack의 top 값을 convExp에 가져다 붙인다. (convExp[idx++] = SPop(&stack))
    • 연산자Stack의 top값과의 비교가 끝났다면, tok을 연산자Stack에 가져다 쌓는다.
      • SPush(&Sstack, tok)

중위 -> 후위: 프로그램 실행

views
  • 위 내용을 통합한 프로그램을 실행

후위 표기법 수식의 계산

views
  • 후위 표기법 수식의 계산은 왼쪽 문자열부터 계산을 한다.
  • 첫 번째 연산자가 등장 할 때, 그 바로 앞 2개의 숫자에 대하여 피연산자로 하여 연산을 수행한다.
  • 따라서 후기 표기법으로 정리된 수식을 차례대로 Stack에 넣고, 숫자가 나올 경우 push를, 연산자가 나올 경우 pop을 2번 하여 해당 연산자로 연산을 수행한다.
  • 연산된 결과는 스택에 다시 넣는다.
  • 올바른 수식이 올바르게 후기 표기법으로 변환 된 경우 항상 연산자 앞에는 두 개의 피연산자가 존재한다.

후위 표기법 수식 계산 프로그램의 구현

views
  • 연산 방법을 정리하자면,
    • 피 연산자는 무조건 스택으로 옮긴다
    • 연산자를 만나면 스택에서 두 개의 피연산자를 꺼내 계산을 한다
    • 계산 결과는 다시 스택에 넣는다
views
  • EvalRPNExp 에는 후위표기법으로 변환된 수식이 저장된 메모리주소가 입력된다.
  • for문에서 문자를 하나씩 꺼내 tok에 저장하고,
    • 문자가 숫자라면 (isdigit(tok)이 1을 반환) push를 통해 stack에 쌓는다.
    • 아니라면(연산자라면) 2번의 pop을 통해 피연산자를 stack에서 꺼낸 후 연산자의 종류에 따른 연산을 수행한다.
  • 연산 과정에서 최종 결과는 다시 stack에 push되어 쌓이게 되므로 마지막엔 stack에서 pop을 통해 최종 결과를 return한다.

후위 표기법 수식 계산 프로그램의 실행

views

계산기 프로그램의 완성

views
  • 위의 과정을 포함하여 전체를 연결하는 InfixCalculator.hInfixCalculator.c를 작성한다.
views
  • 계산에선 원본 식을 보존하기 위해 strcpy를 이용하여 expcpy에 원본 식을 복사하여 연산한다.
  • EvalInfixExp 함수에서 중위 계산식을 후위 계산식으로 변경하여 연산된 최종 수식에 대한 결과를 반환한다.

CH6. 스택 (Stack) 2

|

CH6. 스택 (Stack) 2

6-2 스택의 연결 리스트 기반 구현

문제 6-1

  • 총 5개의 파일로 구성
    • CLinkedList.h, CLinkedList.c: 원형 연결 리스트 구현파일
      • tail 멤버를 갖고 있으며, 꼬리쪽(tail)에 새 노드가 연결되는 변형된 원형 연결 리스트
    • CLLBaseStack.h, CLLBaseStack.c: 스택 구현결과
    • CLLBaseStackMain.c: 메인파일
  • 구현되어있는 원형 연결 리스트 파일을 가져다가 이용하여 스택을 구현

  • CLLBaseStack.h 파일
    • 원형 연결 리스트를 이용한 스택 구조체 정의 및 ADT 정의
#ifndef __CLL_STACK_H__
#define __CLL_STACK_H__

#include "CLinkedList.h"

#define TRUE	1
#define FALSE	0

typedef int Data;

typedef struct _listStack
{
  List * plist;
} ListStack;


typedef ListStack Stack;

void StackInit(Stack * pstack);
int SIsEmpty(Stack * pstack);

void SPush(Stack * pstack, Data data);
Data SPop(Stack * pstack);
Data SPeek(Stack * pstack);

#endif
  • CLLBaseStack.c 파일
    • 원형 연결 리스트를 이용한 스택의 구현
#include <stdio.h>
#include <stdlib.h>
#include "CLinkedList.h"
#include "CLLBaseStack.h"

// 스택의 초기화
void StackInit(Stack * pstack)
{
  pstack->plist = (List*)malloc(sizeof(List)); // 입력된 스택을 구현할 새 원형 연결 리스트를 할당
  ListInit(pstack->plist); // 할당된 새 원형 리스트를 초기화(CLinkedList 구현)
}

// 스택이 비어있는지 판단
int SIsEmpty(Stack * pstack)
{
  if(LCount(pstack->plist)==0) // 스택이 비어있어서 입력된 스택을 구현하는 원형 연결 리스트의 멤버 갯수가 0개면 True 반환
    return TRUE;
  else
    return FALSE;
}

// 스택에 값을 추가
void SPush(Stack * pstack, Data data)
{
  LInsertFront(pstack->plist, data); // LInsertFront 함수를 이용하여 원형 연결 리스트의 앞에 새 값을 추가
}

// 스택의 값을 참조한 후 삭제
Data SPop(Stack * pstack)
{
  Data data; // pop할 값
  LFirst(pstack->plist, &data); // 스택의 첫 번째 값만 참조하면 되므로 LFirst만 필요, 참조를 통한 pstack->plist->cur의 값 참조 위치로 초기화
  LRemove(pstack->plist); // 참조된 값 삭제, LRemove함수는 현재 위치(cur)의 값만 삭제함
  return data; // 참조된 값 반환
}

// 스택의 값을 참조
Data SPeek(Stack * pstack)
{
  Data data;
  LFirst(pstack->plist, &data); // SPop와 동일하지만 삭제가 되지 않음
  return data;
}

6-4 계산기 프로그램 구현

  • 앞에 구현된 Stack의 내용을 갖고 계산기를 구현

구현할 계산기 프로그램의 성격

views
  • 사칙연산 우선순위, 괄호연산을 포함하여 계산의 우선순위를 따져서 계산이 가능해야 함
    • 소괄호를 파악하여 그 부분을 먼저 연산
    • 연산자의 우선순위(+,- 연산보다 *, / 연산 먼저)를 근거로 연산의 순위를 결정
  • 계산기 구현 자체는 Stack의 알고리즘과 별개지만, 알고리즘의 구현에 Stack이 매우 중요하게 쓰임
    • 계산기 연산의 구현만큼 Stack의 활용능력을 잘 보여주는 예시는 없음!

세 가지 수식의 표기법: 전위, 중위, 후위

views
  • 중위 표기법: 연산자를 중간에 표기
    • 수식 내에 연산의 순서에 대한 정보가 담겨있지 않음
    • 상대적으로 구현하기 어려움
  • 전위 표기법: 연산자를 앞에 표기
    • 수식 내에 연산의 순서가 반영되어 있음
  • 후위 표기법: 연산자를 뒤에 표기
    • 수식 내에 연산의 순서가 반영되어 있음
  • 전위/후위 표기법이 상대적으로 구현 난이도가 낮음
    • 전위/후위 표기법은 연산자의 우선순위 뿐만 아니라 소괄호의 우선순위까지 반영되어있음
    • 즉, 나열되어있는 순서대로 연산하면 됨
  • 후위 연산자의 연산방법
    • ex. 중위 표기법 5 + 2 / 7 -> 후위 표기법 5 2 7 / +
      • 2, 7을 /로 연산 후 그 결과와 5를 + 연산
  • 중위 연산자 형태로 받은 입력을 변형하여 후위 표기법으로 만들고, 만들어진 후위 표기법 식을 계산하여 계산기를 완성

중위 -> 후외: 소괄호 고려하지 않는 경우

views
  • 입력으로 저장된 중위 표기법의 수식을 이용하여 후위 표기법으로 변환
  • 입력으로 저장된 수식을 왼쪽 문자부터 시작해서 하나씩 처리해나감
  • 피 연산자를 만나면 무조건 변환된 수식이 위치할 자리로 이동시킴
  • 연산자는 무조건 가운데 쟁반(Stack)으로 우선 이동
views
  • 가운데 쟁반(Stack)으로 보내지는 연산자는 쟁반으로 갈지 아니면 쟁반의 연산자가 변환된 수식이 있는곳으로 옮겨진 다음 새 연산자가 들어갈지 결정해야 함
  • 숫자는 무조건 변환된 수식이 위치할 자리로 이동
views
  • 쟁반(Stack)에는 연산자가 올라가는 경우가 있고 그렇지 않은 경우가 존재
  • 연산자 기준
    • 쟁반(Stack)에 위치한 연산자의 우선순위가 높다면
      • 쟁반에 위치한 연산자를 꺼내서 변환된 수식이 위치할 자리로 옮김
      • 새 연산자는 쟁반으로 옮김
    • 쟁반(Stack)에 위치한 연산자의 우선순위가 낮다면
      • 쟁반에 위치한 연산자의 위에 새 연산자를 쌓는다
    • 즉, 연산자의 우선순위가 낮은게 높은것 위에 있을 수 없음
      • 무조건 우선순위가 낮은것 위에 높은것이 쌓이게 되어야 함
views
  • 본래 수식의 숫자가 다 들어갔으면 마지막으로 쟁반(Stack)에서 차례대로 연산자를 꺼내 붙임

중위 -> 후위: 정리

views
  • 피 연산자는 그냥 옮긴다
  • 연산자는 우선 쟁반(Stack)으로 옮긴다
  • 연산자가 쟁반(Stack)에 있다면, 우선순위를 비교하여 처리방법을 결정한다
    • 쟁반에 들어가려는 연산자가 쟁반에 들어있는 연산자보다 우선순위가 높다면 쟁반에 쌓는다
    • 쟁반에 들어가려는 연산자가 쟁반에 들어있는 연산자보다 우선순위가 낮다면 우선순위가 높은 연산자를 꺼내 변환된 수식의 뒤에 붙인 후 쟁반에 들어가려는 연산자를 넣는다
  • 피연산자를 다 옮긴 후 쟁반에 남아있는 연산자들을 하나씩 꺼내서 모두 옮긴다

중위 -> 후위: 고민 될 수 있는 상황

views
  • 상황 1: 만약 쟁반(Stack)에 동등한 우선순위를 갖는 연산자가 존재할 경우?
    • 기존에 쟁반(Stack)에 들어있던 연산자를 꺼내 변환된 수식에 붙이고, 들어가려는 새 연산자를 넣는다.
  • 상황 2: 만약 쟁반(Stack)에 연산 우선순위대로 쌓여있는 상태에서 새 연산자가 중요도가 stack의 top보다 떨어지면?
    • 기존에 쟁반(Stack)에 들어있던 연산자를 모두 꺼내 변환된 수식에 붙이고, 들어가려는 새 연산자를 넣는다.

중위 -> 후위: 소괄호 고려

views views views
  • 소괄호를 처리하며, 아래 예제는 이해를 위해 소괄호가 맨 앞에 나온 상황에 대한 예시이지만 실제로는 앞에 다른 연산이 있어도 정상 작동함
  • 소괄호 안에 있는 연산자들이 후위 표기법의 수식에서 앞부분에 위치해야 함
  • 이를 위해 소괄호의 시작을 알리는 부분을 newfloor로 하여 쟁반(Stack)에 쌓고, 그 위로 소괄호 안의 연산에 대한 동일 작업 수행
    • newfloor의 역할은 수식에서 “(“가 하게 되며, “)”가 등장하면 소괄호 수식에 대한 연산이 끝난것을 의미
  • 위 그림의 기본논리 숫자 순서대로 계산이 진행됨
    • 식: (1 + 2 * 3) / 4
      • Stack: empty
      • 변환된 수식: empty
    • 1: “(“가 등장(괄호가 시작)하므로 쟁반에 newfloor 생성
      • Stack: (_newfloor <- top
      • 변환된 수식:
    • 2: 1이 변환된 수식으로 이동
      • Stack: (_newfloor <- top
      • 변환된 수식: 1
    • 3: +가 newfloor 위에 쌓임 (newfloor 위는 비어있는 상태이므로 그냥 들어감)
      • Stack: (_newfloor, + <- top
      • 변환된 수식: 1
    • 4: 2가 변환된 수식으로 이동
      • Stack: (_newfloor, + <- top
      • 변환된 수식: 1, 2
    • 5: *가 newfloor 위에 쌓임 (바로 아래의 +보다 우선순위가 높은 연산이므로)
      • Stack: (_newfloor, +, * <- top
      • 변환된 수식: 1, 2
    • 6: 3이 변환된 수식으로 이동
      • Stack: (_newfloor, +, * <- top
      • 변환된 수식: 1, 2, 3
    • 7: “)”가 등장(괄호의 끝)하므로 쟁반에 newfloor 윗부분의 연산에 대한 변환된 수식으로의 이동
      • Stack: empty
      • 변환된 수식: 1, 2, 3, *, +
    • 8: /가 쟁반으로 이동
      • Stack: / <- top
      • 변환된 수식: 1, 2, 3, *, +
    • 9: 4가 변환된 수식으로 이동
      • Stack: / <- top
      • 변환된 수식: 1, 2, 3, *, +, 4
    • 10: 원래 식이 모두 끝났으므로 쟁반(Stack)을 비움
      • Stack: empty
      • 변환된 수식: 1, 2, 3, *, +, 4, /
  • 식에서 “(“은 또 다른 바닥, “)”은 변환되어야 하는 수식의 끝을 의미한다고 생각하면 됨
    • ”)” 연산자를 만나면 “(“를 만날 때 까지 연산자를 이동시키고, 만나면 newfloor를 만들고 새로 그 위에 쌓으면 됨
  • 해당 챕터 연습문제 풀어보기!
    • 후위 표기법의 수식으로 바꾸는

Face detection model 성능 비교(WIDERFace)

|

Face detection model 성능 비교(WIDERFace)

  • 원글: https://medium.com/nodeflux/performance-showdown-of-publicly-available-face-detection-model-7c725747094a
  • Github: https://github.com/nodefluxio/face-detector-benchmark
  • Detection 방법 설명 1: https://medium.com/nodeflux/the-evolution-of-computer-vision-techniques-on-face-detection-part-1-7fb5896aaac0
  • Detection 방법 설명 2: https://medium.com/nodeflux/the-evolution-of-computer-vision-techniques-on-face-detection-part-2-4af3b22df7c2

views
WIDER Face dataset variations

Performance Metrics

  • 각각 face detection 모델에 대한 성능을 측정하며, 성능은 accuracy와 complexity를 측정

Accuracy

  • Object detection과 마찬가지로 average IoU를 측정함(mean Average Precision, mAP)
    • Jaccard overlap으로 정의되는 겹쳐지는 부분에 대한 비율을 측정하여 True Positive 여부를 결정
    • 그 값이 1에 가까울수록 모델이 객체의 위치를 정확하게 추론한 것임
    • 계산되는 IoU의 평균값을 계산함
views
IoU formula
  • Mean averaged precision (mAP) 는 object detector 모델이 얼마나 정확하게 해당 class를 갖는 객체의 위치를 검출하였는가를 측정함. Face detection의 경우 테스트셋에 대하여 face의 위치로 정의된 좌표(Ground Truth, GT)에 얼마나 올바르게 모델이 추론결과 박스를 그렸는지를 측정한다. 일반적인 mAP의 계산은 아래와 같다.
  • True Positive는 모델에 의해 예측된 위치가 face의 위치를 정확하게 예측한 경우의 횟수를 의미한다. False Positive는 모델에 의해 예측된 위치가 잘못 예측된 경우의 횟수를 의미한다.
  • Object detection의 경우, Jaccard overlap이 일정한 threshold 값을 넘었을 때 올바르게 예측된것으로 간주하며, 보통은 0.5, 0.75, 0.95등으로 다양한 기준으로 True Positive 여부를 판단하지만, 보통은 0.5 기준으로 하며 이를 mAP@0.5로 표현한다.

Complexity

  • 정확하게 추론하는 모델의 경우 complexity가 높아 많은 computation cost를 요구한다. 보통은 complexity가 높을수록 processing time(inference time)이 많이 걸리므로 실시간성이 떨어질 수 있다.
  • 이 글에선 model의 complexity를 CPU, GPU, RAM resource의 usage를 기준으로 판단한다. 또한단일 1080p 이미지가 입력으로 들어갔을 때의 inference time을 측정한다. Inference time은 이미지가 입력되고 최종 출력물이 출력될 때 까지의 시간을 기준으로 한다.

Benchmarked Dataset

  • 사용한 데이터셋은 WIDER Face Dataset이며, 32,203개의 이미지에 393,703개의 얼굴 레이블이 존재한다. 하지만 해당 데이터셋은 일반적인 얼굴 외에도 다양한 pose나 scale, occlusion을 갖는다. 따라서 일반적인 detection model에 대한 합리적인 평가가 가능하다.
  • 하지만 dataset에서 test를 제외한 train과 validation 데이터셋만 이용하였으모, 모호한(invalid) GT에 대해선 제외처리하였다. 또한 15*15 픽셀 미만의 크기를 갖는 얼굴에 대해서도 유의미한 정보가 아니라 판단하여 제외시켰다. 따라서 총 16,106개의 이미지에 대해 98,871개의 얼굴정보를 갖고 실험을 진행했다.

Experiment and Result

views
  • 모든 모델을 CPU에서 돌린 결과 (Inference time)
views
  • Model 1, 2는 CPU, Model 3, 4, 5는 GPU로 돌린 실험 결과 (Resource usage)
views
  • 모든 모델을 CPU에서 돌린 결과 (Resource usage)
views
  • 전체 모델에 대한 수치적 실험 결과
    • IoU threshold: 0.5
  • OpenCV Haar Cascade Face Detector
    • Average IOU = 0.219
    • mAP = 0.307
    • Inferencing time (On CPU) : 0.159 s
    • Resource Usage (On CPU)
      • Memory Usage : 414.453 MiB
      • CPU Utilization : 680-730%
  • DLib HOG Face Detector
    • Average IOU = 0.253
    • mAP = 0.365
    • Inferencing time (On CPU) : 0.239 s
    • Resource Usage (On CPU):
      • Memory Usage : 270.777 MiB
      • CPU Utilization : 99-100%
  • DLib CNN MMOD Face Detector
    • Average IOU = 0.286
    • mAP = 0.416
    • Inferencing time (On GPU) : 0.111 s
    • Inferencing time (On CPU) : 4.534 s
    • Resource Usage (On GPU):
      • Memory Usage : 1171.367 MiB
      • GPU Memory Usage : 1037 MiB
      • GPU Core Utilization : 75-90%
      • CPU Utilization : 99-100%
    • Resource Usage (On CPU):
      • Memory Usage : 588.898 MiB
      • CPU Utilization : 250-450%
  • Tensorflow MTCNN Face Detector
    • Average IOU = 0.417
    • mAP = 0.517
    • Inferencing time (On GPU) : 0.699 s
    • Inferencing time (On CPU) : 1.979 s
    • Resource Usage (On GPU):
      • Memory Usage : 2074.180 MiB
      • GPU Memory Usage : 5004 MiB
      • GPU Core Utilization : 10-40%
      • CPU Utilization : 111-120%
    • Resource Usage (On CPU):
      • Memory Usage : 790.129 MiB
      • CPU Utilization : 500-600%
  • Tensorflow Mobilenet SSD Face Detector
    • Average IOU = 0.598
    • mAP = 0.751
    • Inferencing time (On GPU) : 0.0238 s
    • Inferencing time (On CPU) : 0.1650 s
    • Resource Usage (On GPU):
      • Memory Usage : 1967.676 MiB
      • GPU Memory Usage : 502 MiB
      • GPU Core Utilization : 47-58%
      • CPU Utilization : 140-150%
    • Resource Usage (On CPU):
      • Memory Usage : 536.270 MiB
      • CPU Utilization : 670-700%