2022년 10월 8일 토요일

2022 Naver AI Rush

예선

1. 한 사람당 카드 30장이 주어지는데, 카드 종류는 R,G,B로 총 3종류이다.
    각 사람의 점수는 갖고있는 카드의 가장 적은 종류의 카드의 갯수가 점수가 된다.
    (R=15, G=5, B=10 을 갖고 있다면 G=5개가 가장 적기때문에 5점)
    각 사람이 다음 사람들과 교환할 수 있는 몇가지 규칙이 있는데 이것들을 구현하여
    사람들  점수의 합을 구하는 문제이다.
   

2. 문장과 단어가 있는데 문장에 단어를 끼워넣어야 하는데 규칙이 있다.
    이 규칙을 잘 구현하면 되는 문제인데 풀다가 조금 말려서 3번을 먼저 풀고 다시 2번을 풀었다.

3. 배열 100만개와 규칙 4가지가 주어지는데 이 규칙대로 값을 구하는 문제이다. 중간값, 구간합 개념정도만 알면 쉽게 풀 수 있다.

4. 5x5배열이 있고 각  grid마다 방향이 정해져있다. (상하좌우, 2종류의 대각선이였던것 같다.)
   5개의 돌을 놓을 수 있는데 단, 5개의 돌은 각 열과 행에 1개씩 어긋나게 배치해야한다.
   만약 서로 다른 두 돌들이 연속적인 대각선으로 연결이 되어있다면 적절하지 않은것이다.
   모든 경우의 수를 구하는 문제였다.
   

1라운드

- Data imbalance 문제를 해결할 때 under-sampling 방식만 적용한게 컸던것 같다. (참가자분들은 over-sampling방식을 사용) 데이터가 부족해지기 때문에 일반화 측면에서 성능이 낮아지는듯

- Focal loss는 잘 적용한것 같다

- Face detection 전처리를 적용하지 않고 해볼걸 그랬다

- Augmentation을 color, flip 정도로만 적용했는데 다른 augmentation도 적용을 했어야했다.

- 모델 변천사: resnet18 → efficientnet → resnext

- 개인적으로 대회 참여할 시간이 많이 없어서 시도를 많이 못했 (직장인의 한계)

2022년 2월 27일 일요일

if constexpr 구문

 if constexpr 구문은 템플릿 함수가 컴파일 시에 동일한 함수 내에서 여러 범위를 평가할 수 있게 합니다.

1
2
3
4
5
6
7
8
struct Bear
{
    auto roar() const { std::cout << "Roar!\n"; }
};
struct Duck
{
    auto quack() const { std::cout << "Quack!\n"; }
};
cs

위와 같은 Bear와 Duck 클래스가 있을 때 의성어를 내는 메소드를 만든다고 가정해봅시다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct Animal 
{
    virtual ~Animal() {}
    virtual void speak() const {}
};
struct Bear : public Animal
{
    void roar() const { std::cout << "Roar!\n"; }
    void speak() const override { roar(); }
};
struct Duck: public Animal
{
    void quack() const { std::cout << "Quack!\n"; }
    void speak() const override  { quack(); }
};
cs

OOP관점에서 생각하자면 위와 같이 Animal이라는 클래스를 만들어 Bear와 Duck를 상속시켜주고 speak()과 같은 메소드에서 roar(), quack() 메소드를 각각 호출해주는 방법을 생각해볼 수 있습니다.


if constexpr 구문을 사용하면 상속을 사용하지 않고 컴파일 타임에 두 객체의 종류에 맞게 메소드를 호출할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct Bear
{
    void roar() const { std::cout << "Roar!\n"; }
};
struct Duck
{
    void quack() const { std::cout << "Quack!\n"; }
};
 
template <typename Animal>
void speak(const Animal &a)
{
    if constexpr (std::is_same_v<Animal, Bear>)
    {
        a.roar();
    }
    else if constexpr (std::is_same_v<Animal, Duck>)
    {
        a.quack();
    }
}
cs

compile time에 연산이 수행되는 if constexpr 구문을 사용하는게 훨씬 효율적이라고 합니다.

Reference: 고성능을 위한 언어 C++

2022년 2월 18일 금요일

② Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference




이어서 QAT에 대해 더 알아보도록 하겠습니다.
먼저 QAT를 Batch normalizationSkip connection에 어떻게 적용하는지 보도록 하겠습니다.



[Quantization aware training: Batch normalization folding] 

Conv2d+Batch normalization+Activation 형태의 block을 사용하는 모델들이 대중화되었습니다. 이렇게 Batch normalization을 적용할 때 더 빠른 inference를 위해 weights들을 결합(fusing)하는 최적화를 하게 됩니다.
(해당 내용들은 https://lyzqm.blogspot.com/2021/12/foldingfusing-batch-normalization.html 에 정리해놨으니 참고바랍니다.)

QAT를 적용할 때는 fusing된 weights들에 fake quantization을 적용하고 마찬가지로 activation output에대해서도 적용합니다.




[Quantization aware training: Skip connection] 

   

Resnet에서 사용하는 Skip connection도 QAT를 적용할 때도 기존 방식과 유사합니다.
convolution을 거친 output에 fake quantization을 적용하는것 이외에는 다른것이 없습니다.




[Quantization aware training: Experiments]

Mobilenet의 결과입니다. 
QAT를 적용하여 quantization된 8bit 모델이 퀄컴사의 스냅드래곤에서 
모두 같은 성능대비 빠른 inference가 가능한것을 확인할 수 있습니다.




이러한 성능은 Object detection에서도 비슷하게 나타납니다. 
위의 실험은 MobilenetSSD로 COCO data를 학습시키고 벤치마킹한 결과입니다.
mAP지표는 큰 차이는 없지만 속도 측면에서 향상된 모습을 볼 수 있습니다.



[Conclusion] 
지금까지 QAT에 대해 알아봤습니다. 
quantization효과를 학습하는 과정에서 미리 그 효과를 반영하여 학습 후에 quantization을 진행하여도 성능하락이 없도록 만드는 깔끔하고 간단한 방법인것 같습니다.
NVIDIA, Qualcomm, Google과 같은 기업에서 제공해주는 딥러닝 학습 프레임워크에 모두 QAT를 사용할 수 있는 API가 있을 정도로 많이 사용되는 방법이니 직접 코드를 찾아보셔도 좋을것 같습니다.