요약


Breast cancer관련 병리이미지의 분류문제에서, SSL을 활용할 때, 전체 데이터셋을 다활용하지 않고 50%정도 활용해도 전체를 사용한 것과 비등한 결과를 냈음. 또한, 인코더 전체를 다 사용하지 않고 일부레이어만 사용해도 큰 퍼포먼스의 저하가가 발생하지 않고, 오히려 오르는 경우도 관찰됨

 

Introduction


의료인공지능은 그 목적성이 강해, 대게는 지도학습으로 학습됩니다. 지도학습시에는 라벨이 있는 데이터를 많이 필요로하는데, 이 데이터를 만들기위한 비용이 매우 큽니다. 하지만 최근에, 라벨 없는 데이터에서 그 특징을 사전학습하는 자기지도학습(Self-supervised learning)으로 획기적인 성능향상을 많이 보였습니다.

 문제는 이런 SSL을 할 때, 많은 데이터로 활용하다보니, 컴퓨팅 자원, 시간등이 많이 들어갑니다. 본 연구는 SSL할 때, 정말 이런 많은양의 데이터를 넣어 성능이 나오는가? 1) 더 적은 양을 SSL해도 성능이 유지되는 경우는 없는가2) SSL시 사용되는 인코더를 다 쓸필요는 있는가? 3) SSL에 사용되는 Contrastive learning시 샘플링 작업에 따라 성능 개선이 있는가?를 알아보기위한 논문입니다.

 

Methods


  • SSL method for contrastive learning: MoCo v3  
  • Architecture: tiny Swin Transformer

 

MoCo: MoCo을 알기전에, 대조적학습(contrastive learning)을 이해해야합니다. 대조적학습은 인코딩된 샘플 $\{ k_{0}, k_{1},...,k_{N}\}$ 가 있을 때, 각각을 딕셔너리의 키(key)로 고려합니다. 즉, 쿼리(q)에 대해 하나의 positive key가 있고, 그 외에 다른 키들은 negative key로 고려합니다. 이 때, 손실함수는 아래의 손실함수를 사용합니다.

$L_{q}=-log(\frac{\psi(q,k^{+})}{\psi(q,k^{+} + \sum_{i=1}^{K}{\psi(q,k_{i})}})$

용어설명

  • $\psi(q,k^{+})$: q은 이미지(x)로부터 augmentation 1번에 의해 생성된 벡터, $k^{+}$은 같은 이미지(x)로부터 augmetnation 2번에 의해 생성된 벡터를 의미합니다. 둘 다 같은 origin(원본이미지)으로부터 만들어진 vector입니다.
  • $\sum_{i=1}^{K}{\psi(q, k_{i})}$: K은 negative sample을 의미하고 N-1과 동일합니다. 위에서 사용되었던 이미지(x)의 그 외 이미지들을 의미합니다.
  • $N$: 배치사이즈 또는 memory queue의 길이를 의미합니다.

유사도

  • 유사도함수($\psi (x_{1}, x_{2})$): $exp(sim(x_{1},x_{2})/\tau)$ 입니다. sim은 cosine similarity도 가능합니다.여기서 타우($\tau$)은 "temperature scale"이라고 부르며, 클수록 평활해지는 분포의 값을 가집니다(URL)

해석

  유사도 함수($\psi$)은 항상 양의 값을 갖습니다. 따라서, log 내부 값의 값은 항상 0보다 크고 1보다 작은 값을 가집니다. 즉 log값이 (-inf, 0)을 갖기에, 음수(-)을 곱하여 (0, inf)로 손실함수 값을 계산합니다. (query, positive)의 유사도가 클수록(query, negtaive) 유사도가 작을수록 0으로, (query, positive)의 유사도가 작고, (query, negtaive) 유사도가 클수록 inf의 손실함수 값을 갖습니다.

아키텍처

 MoCo에서는 feature collapse을 방지하기위해서, 서로 다른 encoder 2개를 사용하여, query, key을 생성합니다. query을 생성한 인코더의 가중치(파라미터)를 $\theta_{q}$라 하고, k로부터 얻은 $theta_{k}$라고하면, 가중합을하여 파라미터를 업데이트 합니다. m은 모멘텀계수(momentum coefficient)라고 합니다.

$\theta_{k} <- m \theta_{k} + (1-m) \theta_{q} $

 

샘플링방법


1. Semnatically-relevant contrastive learning (SRCL): 추가적인 positive sample을 구하기위한 샘플링 방법

 SRCL와 MoCo의 가장 큰차이는 positive smapling수의 차이가 있습니다. 배치당 positive sample을 1개씩만 얻는 MoCo와 달리, SRCL을 더 많은 수를 뽑습니다. 주요 가정은 같은 슬라이드 이미지에서 나온 패치들은 더 유사성이 높을 테니, negative로 취급하면 안된다라는 것입니다.

SRCL의 positive sampling 방법은 슬라이드 내의 위치정보를 사용하지는 않고, 유사도 기반으로 샘플링하는 방법입니다. 배치 또는 메모리큐에서 코사인유사도가 가장 큰 5개의 패치를 골라, positive sample로 정의하고, 그외를 negative로 정해서 contrastive learning을 합니다. 이 논문에서는 Poistive sample이 5개를 고르려면, 인코더가 충분히 학습되어야한다는 가정하에, 파라미터가 잘 학습된 구간이라고 생각할만한 5에폭 이전까지는 일반적인 contrastive learning을 돌리고, 이후로 SRCL로 학습했습니다.

$L_{q}=-log(\frac{\sum_{j=1}^{S+1}\psi(q,k^{+})}{\sum_{j=1}^{S+1}\psi(q,k^{+}) + \sum_{i=1}^{K}{\psi(q,k_{i})}})$

  • S: 추가적인 positive samples의 수
  • K: negative 샘플의수. (N-S-1). N 배치 또는 매모리 큐의 수, 1은 자기자신 (query), S은 positive sample 수  

 

2. Negative smaplign (N-Sam)

3. Dynamic sampling (DS)

 

실험 및 평가


 

  • 행1-2: tiny Swin Transformer의 스크래치모델로부터 50에폭을 돌리고, AdamW 옵티마이저를 사용했때, 약 에폭은 25회이상돌리고, 데이터는 4%정도만 돌렸음에도 SOTA인 CTransPath와 유사한 성능이 보였습니다. 심지어 일부 테스크에서는 좀 더 좋은 성능을 보였습니다. 카멜레온데이터셋에서는 오히려 성능이 감소하는것을 보였습니다. 저자들은 이 이유는 CTransPath모델이 TCGA로 다장기로 SSL했을 때, Camelyon16 데이터셋이 섞여들어가서, 잘나올수 밖에없다고 합니다.
  • 행1,행3-5: SSL시 사용하는 데이터를 몇프로 까지 줄여도 성능이 유지되는가를 확인했을 떄, 50%정도 까지 사용해도 전체 데이터셋을 사용했던 MoCo-v3와 유사했다고 합니다. 50%면 충분하다는 실험적인 증거로 제시합니다.
  • 행1,행6-10: 인코더를 전체 사용하는게 아니라 몇번쨰 중간레이어을 사용하는 경우에 따라 성능을 비교했습니다. 1, 2번째 레이어에서 가져오는 경우 성능이 저하되었지만, 끝에서 두번쨰(last 2)의 차원을 가져오는 경우는 오히려 성능이 증가함을 보였습니다.
  • 행1, 행11,12,13: 이 실험에서는 Negative sampling(N-Sam)과 Dynamic sampling(DS)가 상대적으로 더 좋은 결과를 냈습니다.

 

결론


유방암의 WSI encoder을 학습을 SSL로 학습할 때, 요구되는 자원을 좀 더 적게 쓸수 있다는 분석을 얻었습니다. 특히, 데이터를 전체 다 사용하지 않더라도 downstream task에서 전체 사용하는 것과 비등한 성능을 보였다는게 놀랍습니다. 또한, 인코더 전체를 굳이사용할필요는 없고, 마지막인코더를  제이하는 경우가 오히려 weakly supervised learning에서 더 나은 성능일 보였습니다.

반응형

요약


  • 메인루틴(Main routine): 메인루틴은 보통 프로그램의 시작점이며, 프로그램의 주 흐름을 담당합니다. 메인루틴은 일련의 작업을 수행하고 다른 서브루틴이나 코루틴을 호출할 수 있습니다. 프로그램이 시작되는 메인코드라고 생각하면 됩니다.
  • 서브루틴(subroutine): 메인루틴에서 호출되는 함수, 또는 서브루틴에서 호출되는 함수들을 의미합니다. 즉, 다른 루틴에서 호출되는 경우를 의미합니다.
  • 코루틴(Coroutine): 메인루틴에서 호출되지만, 코루틴은 실행되는 도중에, 일시중단되어 다시 메인루틴으로 전환되었다가 다시 코루틴으러 전환될 수 있는 "제어흐름"이 가능한 루틴을 의미합니다.

 

코루틴, 메인루틴, 서브루틴은 프로그래밍에서 중요한 개념이며, 이를 이해하는 것은 프로그래밍의 성능을 향상시키는 데 도움이 됩니다. 각각의 이유를 자세히 설명하겠습니다.

  1. 프로그램의 구조 이해(메인루틴/서브루틴의 이해):
    • 메인루틴은 프로그램의 주요 흐름을 담당하며, 프로그램의 시작점입니다. 이를 이해함으로써 프로그램이 어떻게 실행되는지 전반적으로 이해할 수 있습니다.
    • 서브루틴은 재사용 가능한 작은 조각의 코드를 나타냅니다. 이를 통해 코드를 더 모듈화하고 유지 보수하기 쉽게 만들 수 있습니다.
  2. 코드의 재사용과 모듈화(서브루틴/코루틴의 이해):
    • 서브루틴을 사용하면 비슷한 작업을 수행하는 코드를 여러 곳에서 재사용할 수 있습니다. 이로써 코드의 중복을 피하고 유지보수성을 향상시킬 수 있습니다.
    • 코루틴은 서브루틴과 유사하지만, 일시 중단되고 다시 시작될 수 있기 때문에 작업의 흐름을 제어하는 데 유용합니다. 이를 통해 비동기 작업이나 상태 유지가 필요한 작업을 효율적으로 처리할 수 있습니다.
  3. 동시성과 비동기 프로그래밍(코루틴의 의해):
    • 코루틴은 동시성과 비동기 프로그래밍에서 중요한 역할을 합니다. 코루틴을 이용하면 여러 작업을 동시에 실행하고 작업 간에 상태를 공유할 수 있습니다. 이를 통해 복잡한 동작을 효과적으로 조정하고 성능을 향상시킬 수 있습니다.
  4. 효율적인 코드 실행:
    • 코루틴은 작업의 일시 중단과 다시 시작을 가능하게 함으로써 I/O 바운드 작업을 효율적으로 처리할 수 있습니다. 이를 통해 대기 시간이 긴 작업을 실행하는 동안 다른 작업을 수행할 수 있으며, 시스템 자원을 더 효율적으로 활용할 수 있습니다.

이러한 이유들로 인해 코루틴, 메인루틴, 서브루틴을 이해하는 것은 프로그래밍에서 중요한 역할을 합니다. 이러한 개념을 잘 숙지하고 활용함으로써 보다 효율적이고 유연한 코드를 작성할 수 있습니다.

 

메인루틴(Main routine)


메인루틴은 프로그램의 주요 흐름을 담당하는 부분으로, 일반적으로 프로그램의 시작점이며 종료 지점이 될 수도 있습니다. 메인루틴은 전체 프로그램의 실행 흐름을 정의하고 조절하며, 다른 서브루틴이나 함수를 호출하여 작업을 수행하고 그 결과를 처리합니다.

메인루틴은 보통 다음과 같은 역할을 수행합니다:

  1. 프로그램의 시작점: 메인루틴은 일반적으로 프로그램이 실행되면 가장 먼저 실행되는 부분입니다. 프로그램의 시작 지점을 정의하고 초기화 작업을 수행합니다.
  2. 주요 작업 수행: 메인루틴은 프로그램이 수행해야 하는 주요 작업을 정의하고 호출합니다. 이는 사용자의 입력을 받는, 데이터를 처리하는, 다른 모듈이나 서브루틴을 호출하는 등의 작업을 포함할 수 있습니다.
  3. 서브루틴 호출: 메인루틴은 필요에 따라 다른 서브루틴이나 함수를 호출하여 작업을 분할하고 모듈화합니다. 이를 통해 코드를 재사용하고 유지보수성을 향상시킬 수 있습니다.
  4. 프로그램의 종료: 메인루틴은 프로그램이 종료되는 지점을 나타냅니다. 모든 주요 작업이 완료된 후 정리 작업을 수행하고, 필요에 따라 리소스를 해제하고 프로그램을 종료합니다.

간단한 예시로 메인루틴을 파이썬코드로 구현해보겠습니다. 아래와 같이 input으로 이름, 나이를 입력받아 출력하는 프로그램입니다. 다른 모듈에서 호출되는 것 없이 main()함수가 호출되고 종료됩니다. 이 main함수에서 프로그램이 시작되고, 종료되기에, "프로그램의 흐름이 정의"되었다라고 말할 수 있습니다. 즉, main()이 메인루틴이 됩니다.

def main():
    print("프로그램 시작")

    # 사용자 입력 받기
    name = input("이름을 입력하세요: ")
    age = int(input("나이를 입력하세요: "))

    # 정보 출력
    print("입력된 정보:")
    print("이름:", name)
    print("나이:", age)

    # 종료 메시지 출력
    print("프로그램 종료")

if __name__ == "__main__":
    main()

 

서브루틴(Subroutine): 메인루틴이나 서브루틴에서 호출되는 함수


서브루틴은 다른 프로그램에서 호출되는 (=피호출)되는 함수를 의미합니다. 예는 아래와 같습니다. 위의 예시와 같이 main()이 호출되고 종료되기에 메인루틴은 main()이라는 함수입니다. 그리고 이 main()내에서 calculate_sum()이 호출되고 종료됩니다. 즉, main인 메인루틴에 의해서 호출되는 calculate_sum이 서브루틴입니다. 

def main():
    print("메인루틴 시작")
    result = calculate_sum(3, 4)
    print("결과:", result)
    print("메인루틴 종료")

def calculate_sum(a, b): # 서브루틴
    return a + b

if __name__ == "__main__":
    main()

시간에 흐름에 따라, 두 루틴을 시각화하면 아래와 같습니다. 메인루틴이 서브루틴을 호출하고, 그 동안 메인루틴은 서브루틴이 일을 다 처리할 때까지, 잠시 작업대기를 합니다. 그리고, 서브루틴의 일이 끝나면, 메인루틴으로 다시 작업이 넘어가서 메인루틴의 테스크가 종료됩니다.

Figure 1. 시간의 흐름에 따른 메인루틴-서브루틴

 

코루틴(Coroutine): "일시중지 후 재시작"이 가능한 함수


코루틴은 일시 중단되고 다시 시작될 수 있는 함수로, 일반적인 함수와 다르게 실행 중에 일시 중단되어 값을 주고 받을 수 있습니다. 이를 통해 비동기적인 작업이나 상태를 유지하는 작업을 효율적으로 처리할 수 있습니다. 특히, 코루틴은 서브루틴과 다르게 메인루틴으로 작업상태가 넘어가도, 코루틴내 내용을 잃지않고 유지할 수 있습니다. 그리고, 이런 메인루틴->코루틴, 코루틴->메인루틴의 작업상태를 넘겨 실행하는 단계를 "진입점"이라고합니다. 이런 진입점은 서브루틴은 "메인루틴->서브루틴"의 1개의 진입점을 가지지만, 코루틴은 여러 개의 진입점을 가집니다.

 

코루틴을 사용하려면, 코루틴의 성격을 정리해보겠습니다. 

  • 코루틴은 generator의 특별한 형태입니다. (generator도 데이터를 yield와 함께 하나씩 얻어올 수 있고, 그 다음 차례가 그대로 유지되기에 코루틴으로 취급합니다.)
  • 코루틴은 파이썬의 2.5부터 generator로 설명되지만, 실제로 async, await구문과 함께쓰려면 3.5의 이상부터 필요합니다.

그리고, 코루틴의 시작/값보내기/값얻기의 구현방법을 알아야합니다.

  • 코루틴의 시작: 코루틴은 보통 while True 구문을 이용해서 계속해서 일반적으로 코루틴의 시작을 알립니다.
  • 코루틴에 값보내기: 코루틴에 값을 보내는 방법은 코루틴객체.send(객체)로 외부의 값을 코루틴으로 전달합니다.
  • 코루틴 내에서 값 얻기: 이 값을 가져오기 위해서, `yield` 키워드를 이용하여 값을 얻어옵니다.

 

아래의 예시에서는 `searcher()`가 코루틴 함수입니다. 하나 씩 내용을 확인해보겠습니다.

  1. while구문으로 코루틴을 계속 실행하는 상태로 둡니다.
  2. while 구문이 이하가 도는 경우, yield구문에서 값이 올 때까지 기다립니다.
  3. next()가 호출되어야하는 이유는 제너레티너나 코루틴을 실행하고 첫번째 값을 얻기위해 호출합니다. 코루틴은 yield 표현식이 보이면 일시중단됩니다.
  4. 코루틴객체.send(객체)의 구문으로 코루틴에 값을 보내면, 그 보내는것과 동시에 코루틴이 실행됩니다. 다시 while True 구문에 의해서 yield 키워드가 등장하고, 코루틴이 일시중지됩니다. 이와 동시에 다시 메인루틴으로 작업이 옮겨갑니다.

 

def searcher():
    """
    검색어를 받아들이고 출력하는 코루틴
    """
    while True:
        query = yield  # 일시 중단하고 외부로부터 값을 받음
        print("검색어:", query)

# 코루틴 생성
search = searcher()
next(search)  # 코루틴 시작

# 코루틴에 값 전달
search.send("파이썬")
search.send("데이터 분석")

Figure2. 메인루틴-코루틴의 시간에 엔트리포인트 예

또, 다른 예시입니다. 코루틴은 일시정지를 함으로, 코루틴 내의 그 내용을 잃어버리지 않고 유지할 수 있다고 했습니다. 코루틴에서 average, count, total라는 변수를 계속 코루틴에서 들고 있고, send로 더할 때 마다, 코루틴의 변수가 변경되는 것을 확인할 수 있습니다.

def running_average():
    """
    입력된 숫자들의 평균을 계산하는 코루틴
    """
    total = 0
    count = 0
    average = None

    while True:
        value = yield average  # 새로운 값 받기
        total += value
        count += 1
        average = total / count

# 코루틴 생성
calc_average = running_average()
next(calc_average)  # 코루틴 시작

# 코루틴에 숫자 전달하고 평균 계산
print("평균:", calc_average.send(10))  # 출력: 10.0
print("평균:", calc_average.send(20))  # 출력: 15.0
print("평균:", calc_average.send(30))  # 출력: 20.0

 

 

코루틴을 이용한 async, await의 활용


asyncio을 사용해서 비동기적으로 작업을 수행할 수 있습니다. asyncio을 사용하기위해 대표적인 키워드 2개를 살펴보겠습니다.

      • async 키워드: async def로 함수를 정의하면 해당 함수는 코루틴 함수로 선언됩니다. 이는 함수가 비동기적으로 실행될 수 있음을 나타냅니다. 아래와 같이, async def 로 정의된 함수는 인자를 담아 호출하면, coroutine object로 정의됨을 알 수 있습니다.

    • await 키워드: await 키워드는 비동기 작업이 완료될 때까지 대기하고, 해당 작업의 결과를 반환합니다. await 있는 키워드가 실행되면, 이 라인의 구문이 실행되는 동안, "비동기적으로 메인루틴으로 잡이 넘어가고", "완료되면 메인루틴의 작업에서 다시 코루틴으로 작업이 넘어옵"니다.  Q. "async함수를 정의하면 꼭 await구문을 써야하냐?"할수도있는데, 꼭 사용하지 않아도 됩니다만, 비동기작업을 안쓸꺼면 굳이 async로 정의할 필요는 없을 것 같습니다
async def hello():
    print("안녕하세요, 코루틴!")

async def main():
    print("메인 루틴 시작")
    await hello()
    print("메인 루틴 종료")

asyncio.run(main())

 

아래의 예시를 보겠습니다.

  1. `async def`로 fetch_url이라는 함수를 비동기적인 함수로 정의했습니다. 
  2. `await` 키워드를 fetch_url내에 정의합니다. ` fetch_url `이 호출되고 await구문이 있는 라인이 실행되면 sleep이 실행되면서, 작업을 메인루틴으로 넘깁니다.
  3. 메인루틴이 실행중이다가, await구문이 완료되면 그 다음인 return으로 반환됩니다.
  4. `async main`에서는 await asyncio.gather로 비동기작업을 여러개로 구성할 때, 모든 작업이 완료될 때까지 대기해주는 함수입니다. 
import asyncio

async def fetch_url(url):
    print(f"URL 가져오는 중: {url}")
    await asyncio.sleep(1)  # 비동기적으로 1초 동안 대기
    print(f"URL 가져오기 완료: {url}")
    return f"Content of {url}"

async def main():
    tasks = [fetch_url("https://www.example.com"), fetch_url("https://www.google.com")]
    completed_tasks = await asyncio.gather(*tasks)
    print("모든 작업 완료:")
    for result in completed_tasks:
        print(result)

asyncio.run(main())

 

언제쓰면 유용한가? "네트워크 요청", "file I/O bound task", "데이터베이스 쿼리"


  1. 동시성 증가: 비동기 프로그래밍을 사용하면 여러 I/O 작업을 동시에 처리할 수 있습니다. 한 작업이 완료될 때까지 기다리지 않고 다음 작업을 시작할 수 있습니다.
  2. 응답 시간 개선: I/O 바운드 작업을 동시에 처리함으로써 전체 시스템의 응답 시간을 개선할 수 있습니다. 작업이 완료될 때까지 기다리는 동안 다른 작업을 수행하여 전체 처리 시간을 최소화할 수 있습니다.
  3. 자원 효율성: 비동기 프로그래밍을 사용하면 블로킹되는 대기 시간 동안 다른 작업을 수행할 수 있으므로 시스템 자원을 더 효율적으로 활용할 수 있습니다.
  4. 스케일링 용이성: 비동기 프로그래밍을 사용하면 더 많은 동시 요청을 처리할 수 있으며, 이로써 시스템의 스케일링이 용이해집니다.
반응형

GitHub 코드 임베딩은 GitHub Gist를 사용하여 코드 조각을 웹 페이지나 다른 문서에 삽입하는 기술입니다. Gist는 Git 저장소와 비슷하지만 단일 파일 또는 여러 파일의 스니펫을 보관할 수 있는 간단한 방법을 제공합니다. 여기에는 코드, 텍스트 또는 마크다운 문서 등의 다양한 유형의 스니펫을 저장할 수 있습니다.

GitHub Gist를 사용하여 코드를 임베딩하는 일반적인 단계는 다음과 같습니다:

  1. Gist 생성: 먼저 코드 조각을 GitHub Gist에 업로드합니다. 이를 위해 GitHub에 로그인하고 "New gist" 버튼을 클릭하거나, 기존 gist를 수정하여 코드를 추가합니다.
  2. 코드 추가: Gist에 코드를 추가합니다. 파일 이름을 지정하고, 코드를 입력하고, 선택적으로 설명을 추가할 수 있습니다.
  3. Gist 저장: 코드와 설명을 추가한 후, Gist를 저장합니다.
  4. 임베드 코드: Gist를 웹 페이지나 다른 문서에 삽입할 때는 <script>나 <link> 태그를 사용하여 Gist의 URL을 포함시킵니다.

 

사용방법은 아래와 같습니다.

1. gist 을 접속합니다. (GIST 광주과학기술원이 아닙니다)

 

2. gist 접속화면에서 코드를 소스코드로부터 복사 붙여넣기 합니다.

 

3. "Creat secret gist"을 누른후, html의 script src을 가져옵니다.

 

4. 이 코드를 아래와 같이 html로 붙여 넣습니다. 티스토리 같은 경우는 HTML 코드 블럭으로 HTML 코드를 삽입 할 수 있습니다.

 

삽입하고나면, 아래와 같이 렌더링된 코드를 볼 수 있습니다.

 

반응형

요약


 

전자장비에서 처리하는 컬러이미지는 기본 요소인 픽셀(Pixel)을 구성하고, 이 픽셀을 R, G, B 값을 갖습니다. RGB은 컬러가 아니라, 숫자들의 조합을 컬러로 매핑한 컬러코드에 불과합니다. 이 컬러코드를 어떤식으로 매핑했느냐가 RGB, CIEXYZ, CIELAB의 흐름이됩니다. 요약하면 아래와 같습니다.

  • CIE 1931RGB컬러시스템: 사람이 인지적으로 구분할수 있는 컬러코드입니다.
  • CIEXYZ: CIE1931RGB컬러시스템의 빨강의 음수부를 양수로 변환한 컬러리스템입니다.
  • CIELab: 색차(컬러-컬러)간의 차이를 균일하게 만든 표준색공간으로 등간격인 색공간입니다. L(명도, lightness), A*(red & green), B*(Yellow & Blue)로 구성됩니다.

 

사전지식


  • CIE (Commision Internationale de l'Eclairage): 국제조명기구
  • 색도: 색의 정도를 의미합니다. 색은 밝기와 색도로 나누는데 흰색과 회색은 같은 색도이지만, 밝기만 차이납니다.

 

CIE 1931RGB color system


CIE 1931RGB color system은 국제조명기구가 인간의 시각에 기반한 컬러시스템을 만들었습니다. 이 컬러시스템 인간이 최초로 색을 정량적으로 표현한 것입니다.이 컬러시스템은 RGB컬러시스템과 전혀 관계가 없습니다. 단지 사람이 느끼기에, R, G, B 의 광량이 어느정도 합쳐지면 색이 되는지를 표시한것에 불과합니다. 이 실험을 "컬러 매칭 실험"이라고합니다. 또는 등색실험이라고 종종 번역됩니다.

1920년대에 W. 데이빗 라이트(Wright)와 존 길드(Guild)란 사람이 컬러매칭실험을 진행했습니다. 이 컬러매칭 실험은 단색광(Mocochromatic light)을 통해 특정 파장을 하나 빛추고, 실험광(R), 실험광(G), 실험광(B)의 광량을 조절해가면서 합성한후에, 단색광과 합성광이 색이 동일한지 외부관찰자한테 관찰하여, 동일한지만 확인하는 것입니다. 그렇기에, CIE 1931 RGB color system에서 구분될 수 있는 색상은 인간이 실제로 구분할 수 있는 색상수와 동일하다고 종종 여겨집니다. 스크린의 한 쪽에서는 단색광을 비추고(reference color), 다른 한쪽은 관찰자가 R,G,B을 합성해서 단색광과 동일할 때까지 밝기를 조절했습니다. 이후에 CIE 에서 이를 표준으로 채택하면서, 컬러 매칭실험에서 얻어진 광량의 조합을 표시한것이 CIE 1931RGB입니다 [1].

 

컬러매칭실험에서는 하나 문제가 있었습니다. 예상과는 달리, 컬러매칭시에 R,G,B로 어떻게 하더라도 만들 수 없는 색이 있었습니다. 예를 들어, 특정 청록색깔은 R,G,B을 어떻게해서도 합성이 안되었습니다. 이경우에는 테스트 색깔인 청록에 R을 혼합하고, 청록+R을 테스트컬러로 두었습니다. 그리고, 나머지 G와 B의 조합으로 등색을 만들었습니다. 이렇게 RGB와의 혼합량에 의해 나타내는 컬러를 그래프로 나타낸 것을 "색 대응 함수"라고 합니다.  그리고, 이 R,G,B의 각각의 광량을 삼색 자극값(Tristriumulus value)라고합니다(또는 삼자극치...).

이 실험은 사람의 눈에 있는 빛의 수용기인 원추세포(Cone cells)를 기반으로 합니다. 이 원추세포는 3가지 종류(subtype)이 있고, 이 3가지 종류가 각각 다른파장을 인지하도록 되어있습니다. 단파장(Short)은 420nm~440nm, 중파장(M)은 530nm~540nm,이며, 장파장(long) 560nm~580nm입니다. 이 세가지 원추세포를 가중치를 두어 조절하면, 특정 파장길이의 색상을 인지할 수 있습니다. 즉, 어떤 색이든 이 삼원색의 조합으로 다 만들 수 있으니, 단색광 하나와의 비교로 광량을 기록하여 두면 되지 않겠느냐는 것입니다. 이 세가지의 광원의 광량을 "삼자극치(Tristimulus)"라고 합니다. ("삼", "자극", "치"라고 이해하면됩니다)

그림2. CIE 1931 RGB 색 대응 함수 (Wikipedia): X축 단색광의 파장, Y축 각각 필요한 R,G,B 삼원색량의 광

 

이 색 대응함수는 아래와 같습니다. X축은 단색광의 파장을 의미하고, 이 단색광의 파장을 재현하기위한 광량을 Y축에 그린 것입니다. Y축은 절대적인 밝기는 아니고, 상대적인 밝기로 색 대응함수 R,G,B의 아래 면적의합이 모두 1이 되도록 정규화한것입니다. 그래서, 각 RGB은 0에서 1값을 갖습니다. 이 RGB색공간은 컬러매칭을 통해 얻어지는 광량의 조합이기에,  전자장비에서 얻어진 RGB와는 다릅니다.

 

CIE 1931XYZ color system


컬러매칭시험에서 색 대응함수가 음수값을 보이는지점이 있어 ,색재현 등에서 단점으로 꼽혔습니다(그림 2). 그림2에서 r의 색대응함수에서 500nm 파장대가 0이하인 것을 볼수 있습니다. 즉, 이 마이너스 부분을 플러스로 변환한 것이 CIEXYZ입니다. 이 RGB 컬러 매칭함수의 마이너스 값을 없애기 위해서, Dean Judd 마이너스부분을 없앨수 있는 컬러 매칭 함수를 제안했다. 그래서, RGB매칭함수를 XYZ컬러매칭함수로 변환하는 함수가 아래와 같습니다. 

XYZ 컬러 공간에서도 좌표는 각 컬러가 0에서 1사이 입니다. 

이 XYZ 컬러 공간은 아래와 같이 특징을 가집니다.

  • CIE RGB색공간을 단순히 선형변환 한 것입니다.
  • X,Y,Z의 모든 값이 0또는 양수 값을 가집니다.
  • 인간이 볼 수 있는 색은 X, Y, Z의 [0, 1]로 이루어져야한다.
  • 흰색은 x=y=z=1/3인 지점으로 정의되어야한다.

이 XYZ컬러 공간은 CIE RGB color system의 얻어진 RGB 이므로, 전자장비에서 처리되는 RGB값을 매칭해도 의미가 없습니다.

 

 

CIE xyY Color system


CIE RGB로부터 CIE XYZ로 컬러매칭함수를 변경했습니다. 이제는 XYZ가 삼자극치가 됩니다. 이 X, Y, Z을 다른 공간인 xyz에 매핑할 수 있습니다. 

아래의 그림에서보면, XYZ좌표가 있고, 각 X, Y, Z의 점은 RGB가 적절하게 섞인 값이고 각 축이 1이며, 나머지는 0인좌표들입니다([1,0,0], [0, 1, 0], [0, 0, 1]). 근데 이공간을 잘보면, Y축에 대해서 XY평면에 정사영(Projection)시키고, 그 밝기(또는 조도)를 Y축이라고하면 색깔을 똑같이 얻을 수 있습니다.

그림4. https://mymusing.co/cie-rgb-and-cie-xyz-color-space/

 

x = X/(X+Y+Z)
y = Y/(X+Y+Z)
z = Z/(X+Y+Z)  # z=1-x-y

이렇게 정사영된 좌표를 xyY color space라고 합니다. 아래의 수식과 같이 X Y Z의 합이 1이도록 만들들면, x+y+z=1인 등식이 하나 생겨서 z=1-x-y가 될 수 있고, 이 때의 소문자 x,y와, 대문자 Y(조도)로 구성된 컬러공간이 xyY clor space입니다.

그렇기에 아래와 같은 CIE Chromaticity diagram에서는 x축이 한색깔이 아니라 여러색깔을 의미하고, 이에 해당하는 소문자 x, y축 또한 한 색깔이아닌 여러색깔이며 소문자 y축으로 표현합니다. 그리고, XYZ color space와 동일하게 정사영된것뿐이므로, 중앙은 흰색이됩니다. 그리고, 아래의 chromaticity diagram에서는 색만 보기에, 명도(밝기)인 Y가 없어 보이지 않습니다. 

CIE Chromacitiy diagram

 

HSV color system


HSV color system은 색을 표현하는 또 하나의 방법입니다. 색상(Hue), 채도(Saturation) 명도(value)를 각각 이용하여 색상을 표현합니다. 이 세가지 개념은 아래와 같습니다.

  • 색상(hue): 가장 기본적인 색을 빨강으로 하여, 빨강을 기준으로 0도로 각 스펙트럼을 360도로 표현합니다. 360도와 0도에 위치하는 색상은 모두 빨강입니다. 이 색상(hue)은 학문적으론, 빨강 노랑, 초록, 파랑 중 하나 또는 이 두가지의 색의 조합과 유사하게보이는 시각적인 감각이라고 합니다. 즉, 순수한 색에서의 다양성만 의미합니다[3] . 
  • 채도(Saturation): 채도는 가장 진한 상태를 100%로 하는 색상의 진한정도입니다. 흔히, 진한 빨강와 연한 빨강이라고 색상의 진한정도를 표현하듯이, 이는 채도를 의미합니다. 아래의 그림6과 같이, 진한 빨강화 연한 빨강 (세탁한 다음에 물빠진 옷 같은 색상)과 같이 사람은 색상의 진한 정도를 파악할 수 있고, 이 개념을 채도라고 합니다.
  • 명도(Value): 색상의 밝음과 어두움을 나타내며, 모든 색상의 명도가 0은 검정입니다. 단 모든 색상의 명도가 100%인 경우는 흰색은 아닙니다.

HSV의 탄생배경은 대부분의 컴퓨터나 텔레비전의 디스플레이에서 사용되는 광원은 RGB각각의 광원의 강도로 조절이 가능합니다(이를 RGB additive primary color system)이라고합니다. 이 RGB은 색공간(Gamut)에서 모두가 R,G,B광원의 강도로 비슷하게하니 재현성은 좋았지만, 예술계쪽에서 사용하기 직관적이지 않았습니다. 일반인들은 와닿지 않지만, 아티스트들은 색조, 음영에 기반하여 작업하기때문에, 이를 H,S,V에 재매핑 할 필요가 있었습니다. 그림6과 같이 아티스트들은 어떤색에, 흰색을 조합한 것을 "틴트", 검정과의 조합은 "쉐이드", 이 두 조합을 "톤"이라고 구분하여 색상을 정하고 있었는데, RGB은 다른 표현방식이기에 직관적이지 않습니다. 예를 들어, 그림7과 같이 오렌지색의 색감을 줄이고싶은데, R,G,B에서는 각각 얼마나 줄여야하는지 와닿지 않습니다. 하지만 HSV에서는 Saturation만 줄이면되기에 상대적으로 더 직관적입니다. 즉, 인간이 느끼는 색의 변화가 HSV가 보다 직관적이고, HSV에서 원하는 색을 찾기위한 가감(additive, subtract)가 더 계산이 편하기에 HSV가 1970년대에 제시되었습니다. (동일한 문제를 제기하면서, HSL color system도 비슷한시기에 제안되었습니다)

그림6. https://en.wikipedia.org/wiki/HSL_and_HSV
그림7. 오렌지색의 색감을 절반으로 떨어뜨리고자할 때의 RGB 변화

보통 HSV color system은 원뿔형 또는 원기둥형으로 나타내기도 합니다. 원기둥형과 원뿔형 모두에서 각 각도는 색상을 의미합니다. 위에서 언급했듯이, 명도가 충분히 밝지 않으면 모든 색깔은 검정이기에 원뿔형을 나타냅니다. 반대로 말하면, 색상이 진하지 않으면 명도의 변화게 크게 됩니다. 이와 다르게 원기둥 모형에서는 각 색상의 명도가 0인부분이 중복되어 검정으로 표시되며, 원뿔형의 여집합에 해당하는 부분은 사실상 중복이됩니다. 

Reference

Fairman H.S., Brill M.H., Hemmendinger H. (February 1997). "How the CIE 1931 Color-Matching Functions Were Derived from the Wright–Guild Data". Color Research and Application 22 (1): 11-23. 및 Fairman H.S., Brill M.H., Hemmendinger H. (August 1998). "Erratum: How the CIE 1931 Color-Matching Functions Were Derived from the Wright–Guild Data". Color Research and Application 23 (4): 259.

https://en.wikipedia.org/wiki/HSL_and_HSV 

[3] Fairchild (2005), pp. 83–93

반응형

 


CLIP은 2021년에 언어-이미지 쌍을 사전학습하는 방법을 제시한 연구입니다. (이미지, 이미지에 대한 텍스트)를 쌍으로 대조적학습(Contrastive learning)을 사전학습하여, 자연어처리에서와의 접근방식과 유사하게 비전문제에서도 사전학습결과후, 각 테스크에서 좋은 성능을 낼 수 있음을 보여줍니다.

 

Preliminary


  • Zero shot learning(제로샷 러닝): 모델이 훈련 데이터에서 직접 학습하지 않은 클래스 또는 레이블을 인식하고 분류할 수 있게 하는 학습방법

 

 

Introduction


언어모델에서의 MLM(Masked language model)같은 특정 테스트와 무관한 학습을 하는 것으로 자연어쪽에서는 성공적인 연구성과를 보여주었습니다. 흔히, 일반화된 아키텍처(General model)에서 다운스트림으로 제로샷(Zero shot)으로 전이학습하기가 용이해져서, 아키텍처에 예측할 해더(분류기레이어)등을 추가할필요가 없어져왔습니다. 그 결과로 GPT-3같이 여러 목적에도 다양하게 사용할 수 있는 모델이 데이터가 부족해도 사용할 수 있게 되었습니다. 이런 학습방법은 웹에서 수집한 데이터로 사전학습을 하니, 라벨링을 직접하지 않습니다. 그럼에도, 크라우드소싱으로 라벨링한 것보다 좋은 성능을 보였고, 사전학습을 하는 것이 표준적인 관행이 되었습니다. 하지만, 비전문제에서도 이런 접근방법이 가능한지에 대해서는 아직 연구된바 없었습니다. 특히, 비전문제에서는 보통 클라우드 라벨링으로 모은 ImageNet으로 사전학습데이터를 사용하는데, 웹에서 사용한 데이터로 이런 사전학습을 할 수 없을까?가 이 연구의 motivation입니다.

 

여태까지는, 이미지를 표현할 때, 언어를 이용한 표현을 해볼 수 있겠는지와 관련된 연구는 별로 없었습니다. 실제로, 이전의 연구성과들에서는성능이 거의 안나왔었습니다 (11.5% 정확도, 이 때, SOTA가 88.4%). 이후, 인스타그램 이미지에서 ImageNet 관련 이미지 해시테그를 예측 하는 사전학습을 이용하자, SOTA보다 더 좋은 결과들을 보였습니다 (역시, 데이터가 많아야..). 이후에도 비슷한 접근방식으로 JFT-300M dataset을 만들어 사전학습하여 성능향상을 보였던 논문도 있었습니다. 하지만, 이런 방식도 결국 클레스를 N개로 정하다보니, "zero-shot"의 학습성능을 제한한다는 것에 한계를 제안합니다(결국 softmax하여 분류기를 추가하기에 일반화 목적성능의 달성이 어려움). 생각해보면, 자연어는 이미지보다 더 많은 클레스의 표현이 가능하기에, 이런 지도학습에도 더 도움을 줄 수 있을 것 같습니다. 

 

CLIP 방법론: 이미지 표현 < 자연어표현이 더 풍부, 자연어기반 지도학습으로 zero-shot구현


CLIP의 핵심아이디어는 이미지의 인식방법이 자연어로부터 배울 수 있다는 것입니다. 이를 위해 아래의 단계대로 실험을 진행했습니다.

  1. 대규모 데이터셋 구축(creating a sufficient large dataset)
  2. CLIP(Pre-training method) 구성(selecting an efficient pretraining method)
  3. 모델 크기 선택(Choosing and scaling a model)
  4. 학습(Training)

 

 

1. 대규모 데이터셋구축

새로운 아이디어는 아니고, 이전에는 N-gram이나 크라우드소싱데이터를 이용했다면, CLIP에서는 대규모데이터를 학습하려 노력했습니다. 자연어로 지도가 됨을 보여주기 위해서, 대규모데이터가 필요했습니다. 

특히 1) 직접 클레스에 라벨링이 아니라 이미지에 대한 설명을 학습해볼 수도 있는 것이구요(이미지의 특징의 학습이 자연어로 지도됨). 2) 라벨링이 되어있지 않아도, 온라인상에서 일반적인 더 큰 이미지를 구축하려고 노력했습니다. ImageNet이나 MS-COCO 데이터셋 10만장밖에안되고, YFCC100M 데이터도 100million이지만 데이터 퀄리티가 좋지 않아(title, 설명등이 미포함), 전처리하고나면 ImageNet정도 사이즈밖에안되서 적다고합니다. 실제로 OpenAI에서는 4억장의 이미지-텍스트 쌍을 인터넷에서 구했다고 합니다. 이미지를 얻기위해서 5만개의 쿼리를 사용했고, 클레스 벨런스때문에, 쿼리당 20,000개의 이미지-텍스트 쌍을 넘지않도록했다고합니다. 이 데이터셋을 WebImageText(WIT)라 명명했습니다.

 

 

2. CLIP(Pre-training method) 

CLIP은 효율적인 사전학습을 찾으려고 노력한 결과물입니다. 연구 초기에 사전학습방식을 결정할때, 아래와 같은 3가지의 방법을 적용해보았습니다. 3가지 목적으로 각각 이미지는 ViT(Vision Transformer)로 인코딩하고, 텍스트는 Transformer로 인코딩한다음에 이미지의 캡션을 예측하도록 Joint traning을 진행했습니다. 각 3가지는 Transformer Language model은 언어 생성, Bag of words은 순서가 없는, Bag of words contrasative (CLIP)은 BOW에 대조적학습까지 적용한 방법입니다. 같은 파라미터라면, 성능은 CLIP이 훨씬 효율이 높아 CLIP으로 사전학습을 하는 것을 목표로 했습니다.

 

CLIP은 N개의 배치로 튜플인 (이미지, 텍스트)의 페어를 입력을 받습니다. 만일 N개가 배치로 들어온다면, N x N의 메트릭스를 만들 수 있고, N개의 이미지와 N개의 텍스트 각각 하나씩만 실제 연관된 이미지-텍스트여서 N개의 Positive로 정의하고, $N^{2}-N$의 negative 페어를 만들어냅니다. 이를 대조적학습으로 학습하는 방법입니다 (아래의 Figure 3). 

Figure 3. Numpy-like한 슈도코드

3. 모델 크기 선택(Choosing and scaling a model)

이미지 인코딩을 위한 아키텍처를 고를때는 처음에는 ResNet-50을 고르고 일부 변형했다고 합니다. 이는 이미 일반적으로 성공적인 성능을 보여왔기에 첫 선택이었다고 합니다. 그리고, 두 번째로는 ViT을 이용했다고합니다. 텍스트 인코딩은 Transformer을 이용했고, 65M Parameter + 12 Layer + 8 attention head + BPE 인코딩으로 얻어진 voca size은 49,152사이즈입니다.

4. 학습(Training)

학습은 ResNet 시리즈 5개(ResNet-50, 101 Efficientnet 아류 3개)랑 Vision transformer (B/32, B/16, L/14)을 학습했습니다. 그외엔 하이퍼파라미터는 Grid search/랜덤서치로 1에폭에 했구요.

 

결과


본문을 참고하시면 좋을 것 같습니다. 아래와 같은 내용이 담겨있습니다.

  1. Zero-shot Transfer 하는 경우 이미지분류에서도 압도적인 성능을 낼 수 있음
  2. 프롬프트엔지니어링하면, 기존대비 5%p이상의 성능을 낼 수 있음
  3. CLIP이 잘되는 /잘안되는 데이터셋마다 차이를 보이긴 함
  4. Linear probe(인코더+분류기 레이어 부착후 재학습)한 경우도 매우 좋음

 

사용예시


CLIP official github에 제시된 것처럼 clone 후에, jupyter notebook에서 모델을 다운로드 받아보겠습니다.

import torch
import clip
from PIL import Image

device = "cuda" if torch.cuda.is_available() else "cpu"
model, preprocess = clip.load("ViT-B/32", device=device)

 

CLIP.png은 아래와 같은 다이어그램입니다

위와 같은 다이어그램을 preprocessing (resize->CenterCrop -> RGB -> 텐서변환->정규화)를 거쳐 전처리하고 CLIP에 포워딩해서 백터를 얻어보겠습니다. 또한, CLIP에 넣을 3개의 배치로 텍스트를 넣어보겠습니다. 텍스트는 토큰화되어서 (3, 77)의 77개의 시퀀싱을 갖는 백터며, 각 순서에 맞춰 인덱싱으로 원소가 구성되어있습니다.

image = preprocess(Image.open("CLIP.png")).unsqueeze(0).to(device)
text = clip.tokenize(["a diagram", "a dog", "a cat"]).to(device) # torch.Size([3, 77])


------
print(model.visual) # Vision transformer은 768차원 반환하지만 추가로, 512차원으로 embedding해주는 레이어가 부착되어있습니다.
VisionTransformer(
  (conv1): Conv2d(3, 768, kernel_size=(32, 32), stride=(32, 32), bias=False)
  (ln_pre): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
  (transformer): Transformer(
  ...
  (ln_post): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
 )
 
-----
print(model.transformer) # transformer은 512차원의 벡터를 반환합니다.
Transformer(
  (resblocks): Sequential(
  ...
  (ln_2): LayerNorm((512,), eps=1e-05, elementwise_affine=True)
    )
  )
)

 

CLIP논문에 맞춰, 이미지 표현(image_features), 텍스트 피쳐(text_features)을 구하고, 정규화 후 코사인유사도를 계산해보겠습니다. 유사도 계산후, 유사도(logit)으로 3차원의 벡터가 얻어졌고, "CLIP.png"와 "a diagram", "a dog", "a cat"의 각각의 쌍(pairwise)의 유사도가 [25.5528, 20.0899, 19.7495]로 얻어졌습니다. softmax로 구하면, 0.9928로 0번째 텍스트와 가장 유사도가 높음을 알 수 있습니다. CLIP은 이와 같이 이미지를 가장 잘 설명할 수 있는 text와의 유사도를 구하는 방식입니다.

# normalized features
image_features = image_features / image_features.norm(dim=1, keepdim=True)
text_features = text_features / text_features.norm(dim=1, keepdim=True)

# cosine similarity as logits
logit_scale = model.logit_scale.exp()
logits_per_image = logit_scale * image_features @ text_features.t()  # (1, 512) @ (512, 3)
logits_per_text = logits_per_image.t()

print(logits_per_image.shape, logits_per_text.shape)
print(logits_per_image)
print(logits_per_image.softmax(dim=-1))
# torch.Size([1, 3]) torch.Size([3, 1])
# tensor([[25.5528, 20.0899, 19.7495]], grad_fn=<MmBackward0>)
# tensor([[0.9928, 0.0042, 0.0030]], grad_fn=<SoftmaxBackward0>)

 

그외 사례에서도 아래와같이 적용해보면, 약학정보원에서 얻은 약(타이레놀)과 가장 설명이 유사한 텍스트를 잘 찾아줌을 확인할 수 있습니다.

반응형

 

요약


CNN을 이용한 영상분류에서는 사후해석으로 CAM, Grad-CAM등이 사용됩니다. 이 논문은 CAM방법론들에서 사용하는 Gradient을 이용하지 않고, Activation map에서의 가중치를 직접 획득하는 방식으로, 점수를 직접 산합니다.

 

Introduction: CAM-based explantation에서 사용하는 gradient을 해석에 충분한 방법이 못된다.


Score-CAM을 이해하기위해선 CAM부터 이해해야합니다. CAM은 GAP(Global average pooling)레이어가 꼭 포함되어야하는 방법론입니다 (Figure 1). Activation map(l-1번째 레이어) 이후에 GAP(l번쨰 레이어)가 들어가고 GAP 이후에 Fully connected layer(l+1번째 레이어)가 포함되어있습니다.

Figure 1. CAM을 사용하기위한 GAP가 포함된 네트워크 구성

이때, CAM으로 1번 클레스의 가중치를 구하려면, Activation map(L-1번째 레이어)의 각 채널(k)에서 얻어진 feature map에 GAP 이후의 연결되는 가중치($a_{k}^{c=1}$)에 대해서 선형결합후에 ReLU만 씌우면 됩니다. 그래서, CAM은 아래와 같이 계산합니다. 

$L^{c}_{CAM}=ReLU(\sum_{k}a_{k}^{c}A^{k}_{l-1})$

CAM은 각 activation map($A_{l}^{k}$)가 각각 채널별로 다른 공간정보를 담고 있다고 가정하여, 각 채널이 클레스에 미치는 가중치($a_{k}^{c}$)을 다시 선형결합해서 곱해줍니다. 하지만, CAM의 문제는 1) GAP layer가 없는 네트워크가 없는 경우에 사용할 수 없고, 2) fully connected layer가 없거나, 2개 이상인 경우에는 가중치($a_{k}^{c}$)을 구할 수 없기에 연산이 불가능합니다.

 

 

이후, 위의 문제를 개선한 Grad-CAM(2017)이 연구되었습니다. Grad-CAM가중치($a_{k}^{c}$)을 얻기위해서, gradient을 이용하여 아래와 같이 계산합니다. 차이점은 Activation map이($A^{k}_{l-1}$)에서 Activation map($A^{k}_{l-1}$)로 변경되었다는 것과 가중치($a_{k}^{c}$)의 정의가 아래와 같이 변경되었다는 것 입니다. 이 때문에 1) 네트워크의 global average pooling이 꼭 포함될 필요가 없으며, 2) fulley connected layer가 여럿이 포함되어도 됩니다.

$L^{c}_{Grad-CAM}=ReLU(\sum_{k} a_{k}^{c}A^{k}_{l} )$

where $a_{k}^{c}=GP(\frac{\partial Y^{c}}{\partial A_{l}^{k}})$ (GP은 global pooling operation)

Grad-CAM이나 Grad-CAM++이나 가중치($a_{k}^{c}$)을 gradient을 이용해서 정의하여 구합니다. Y(confidence)에 대한 예측을 gradient로 각 채널로 구하는 것이니 fully connected가 몇개여도 가능합니다. 각 채널별로의 중요도를 미분을 이용한 가중치($a_{k}^{c}$)로 구하는 것이죠. 하지만, 이 Grad-CAM도 "채널별 중요도"에 문제가 있습니다.

 

 


Gradient을 이용하는 경우의 문제점은 중요도를 정확히 계산할 수 없다는 것입니다. 이 이유를 2가지로 삼습니다: Saturation문제False confidence문제

1) Saturation문제는 gradient가 시각적으로 확인할 때, 중요점이 깔끔하게 떨어지지 앟는다는 문제입니다. 딥러닝 네트워크의 gradient가 꽤 노이즈(0이 아닌 소수점이 꽤 많은...)가 있기에, sigmoid function등을 쓰더라도 잔류하는 gradient가 많습니다. 그렇기에 Gradient based 방법론을 쓰면 지저분히 heatmap이 남습니다. sigmoid function 의 입력이 -2라고 하더라도 남고, -5라도 하더라도 뭔가 남고 -10이라고 하더라도 뭔가 소수점이 계속 남습니다.

 

Sigmoid function
Figure 4. Gradient 방법론이 꽤 노이지한 이유를 Saturation문제라고 하며, 근거로 든 figure

 

2) False confidence 문제는 Grad-CAM 자체가 각채널별 Activation map($A^{k}_{l}$)과 가중치($a_{k}^{c}$)의 선형결합이기에, 생기는 문제입니다. Grad-CAM에서는 activaton map의 가중치인 각 i번쨰, j번째 채널에 대해서, 두 가중치$a_{i}^{c}$ > $a_{j}^{c}$가 있는 상황이면 i번째 activation map ($A^{i}_{l}$) 이 더 중요하고, 더 예측에 많이 기여했을거라고 생각합니다. 하지만, 반례가 많이 보인것이 grad-cam의 모습이었습니다. 아래의 그림에서, (2)번의 그림은 가중치($a_{k}^{c}$)가 제일 컷음에도 실제 confidence값 기여에는  0.003으로 낮게 기여한 경우였습니다. 아마도 이 이유를 GP와 gradient vanishing문제떄문이라고 생각합니다. 이 때문에, gradient을 이용하지 않는 해석방법론을 연구한 듯 합니다.

 

Score-CAM: 모델 컨피던스를 증가시키는 것이 중요도 (Channel-wise increase of confidence, CIC)


모델 컨피던스는 인공지능모델이 예측에 어느정도 강한 예측을 보이는 정도이며 통상 [0, 1]로 정규화해서 얻어지는 값입니다(흔히, 확률이라고 부르기도 하는데 정확히는 확률은 아닙니다). 아무튼 Score-CAM은 이 모델의 반환값(model confidence)가 얼마만큼 증가하는지를 확인하고자하는 것 입니다. Score-CAM은 Increase of confidence의 정의부터 출발합니다.

Increase of confidence의 계산은 베이스라인 이미지에 대비해서 Activation이 활성화된 영역만을 남기고, 나머지부분을 마스킹 했을 때, 예측력이 얼마만큼 confidence을 올리는지 계산하는 방식입니다. 일반적인 CNN이 아니라, 벡터를 넣어 스칼라값을 뱉는 인공지능이라고 생각해서 일반화된 Increase of confidence은 아래와 같이 계산합니다.

$c_{i}=f(X_{b} \circ H_{i}) - f(X_{b}) $

  • $ H_{i} $: 이미지랑 같은 크기의 벡터이고, unmasking 용으로 사용됩니다.

*Hadamard Product: 요소별

 

위의 개념을 CNN에 도입해서 Channel-wise Increase of Confidence (CIC)을 계산합니다.


$C(A_{l}^{k})=f(X \circ H_{l}^{k}) - f(X_{b})$


  • $A_{l}^{k}$:l번째 convolution의 k채널의 Activation map을 의미합니다.
  • $X_{b}$: 베이스라인 이미지 입니다.
  • Up(): CNN이 통과되면서 작아진 사이즈를 다시 원래이미지에 맞춰 업샘플링하는 연산입니다.
  • s(): 매트릭스의 원소 값을 [0, 1]로 정규화하는 함수입니다. min-max scaler로 사용합니다. 실제로 activation을 넣어 $A_{l}^{k})=\frac{A_{l}^{k}-minA_{l}^{k}}{maxA_{l}^{k}-minA_{l}^{k}}$로 사용됩니다. 

Activation이 얼마만큼 기여하는지 정했다면 이제 Score-CAM을 정리할 차례입니다. Score-CAM이라고 (9)번식이 Grad-CAM과 다르지않습니다. 다만 10번의 가중치($a_{k}^{c}$)가 달라진 것이 큽니다.

$L^{c}_{Score-CAM}=ReLU(\sum_{k} a_{k}^{c}A^{k}_{l} )$  (9)

$a_{k}^{c}=C(A_{l}^{k})$ where C() denotes teh CIC score for activation map $ A_{l}^{k} $

 

위의 내용을 모두 정리하여, 아래의 알고리즘과 같이 정리할 수 있습니다.

  1. Activation을 구하기 위해서 X이미지를 딥러닝의 CNN에 forwarding하서 $A_{l}$을 구합니다.
  2.  $A_{l}$개수 만큼 C(채널)을 정의합니다.
  3. 각 C(채널)만큼 Activation map($A_{l}^{k}$)을 업샘플링하여 원본사이즈에 맞춘 M을 구합니다.
  4. activation map을 정규화합니다.  # $M_{l}^{k}=s(M_{l}^{k})$
  5. 정규화한 이미지랑 원본이미지랑 요소별 곱하여 리스트 M에 저장해둡니다. // Figure 3의 Phase 1의 완료
  6. 원본이미지에 Activation을 곱한 것을 딥러닝에 다시 태워서 logit 값의 차이를 구합니다. $S^{c}$
  7. 채널별로 얻어진  $S^{c}$ 을 softmax하여 합이 1이 되도록 맞춘 가중치 $a_{k}^{c}$을 구합니다.
  8. Acitvationmap과 선형결합하여 Score-CAM을 구합니다.

 

결과:


첫 번째 결과로, 정성적(Qualitative)로 사례기반으로 ScoreCAM이 다른 gradient방법론보다 노이즈가 적음을 보여줍니다. 아무래도 gradient 방법론들이 saturation problem이 있기 때문이라고 생각합니다.

두 번째 결과로, 하나의 타깃이 아니라 여러 타깃에도 더 객체를 또렷하게 구분하여 히트맵을 보여주었습니다.

그외에도 Deletetion, Insertion을 픽셀값을 중요도순으로 열거하고, Deletion curve에서는 얼마만큼 성능이 빨리 저하되는지, Insertion curve에서는 중요순으로 추가될수록 얼마만큼 빨리 오르는지를 정량적으로 보여주었습니다. X축 비율, Y축은 AUC입니다.

 

응용 사례


의학의 적용분야중, 자폐진단에 안저검사이미지가 얼마만큼 예측할 수 있는지 가능성(Feasbility)을 보여준 논문에서도, 이 연구가 정말 가능하다라는 결과를 보여주기위해 Score-CAM을 보여주었습니다. (A)은 이미지를 지워가면서 AUROC을 보여주었고, B은 Score-CAM결과에서 Score-CAM결과를 삭제해가면서 biomedical domain에 의미가 있는 영역이 분류에 중요한지를 보여주는 이미지입니다(링크).

반응형

+ Recent posts