Transformers

지난번에 트랜스포머 논문을 처음부터 끝까지 열심히 읽고 리뷰를 포스팅했었습니다.

https://smartest-suri.tistory.com/48

 

딥러닝 | Attention is all you need - 트랜스포머(2017) 논문 리뷰

[참고] 본 포스팅은 수리링 본인이 Attention is all you need 논문을 처음부터 끝까지 직접 읽으며 분석하고 리뷰하여 작성했습니다. 불펌 절대 금지! 본문 내용에 잘못된 부분이 있다면 댓글 달아주

smartest-suri.tistory.com


오늘은 Transformer팀의 Huggingface 페이지를 방문해서 트랜스포머의 무궁무진한 발전가능성을 체험해보겠습니다.

https://huggingface.co/docs/transformers/task_summary

 

What 🤗 Transformers can do

Reinforcement learning models

huggingface.co

위 링크를 클릭하시면 Transformer huggingface 다큐멘테이션 페이지 중에서 Conceptual Guidelienes - What 🤗 Transformers can do 코너로 이동하는데요.

설정에서 언어를 KO로 바꾸시면 한국어로도 보실 수 있습니다. 번역이 어색해서 저는 영어로 봅니다.

트랜스포머는 자연어처리(NLP), 컴퓨터 비전, 오디오 및 음성 처리 작업에 사용할 수 있는 pre-trained된 최첨단(SoTA) 모델 라이브러리라고 밝히고 있습니다. 이 페이지에서 간단하게 트랜스포머의 멋진 기능을 체험해볼 수 있는 짧은 코드를 소개하고 있습니다. (보시면 아시겠지만. ... 짱 쉬워요)

이렇게 pipeline을 사용해서 pre-trained된 트랜스포머 모델의 여러 기능을 체험해볼 수 있도록 다양한 코드가 제공되고 있었어요. 저는 이 파이프라인 중에서 객체 탐지 기능을 골라 가지고 놀면서 기능 맛보기를 통해 트랜스포머와 더욱 친숙해지는 시간을 가져보았습니다.


파이프라인이란?

허깅페이스 파이프라인(Hugging Face Pipelines)은 자연어 처리(NLP) 작업을 쉽게 수행할 수 있도록 허깅페이스에서 제공하는 API입니다. 이 API를 사용하면 복잡한 모델 로드 및 전처리 작업 없이도 다양한 NLP 작업을 빠르고 간편하게 수행할 수 있습니다.

  •  pipeline은 transformers 라이브러리의 가장 기본 객체
  •  사용 전에 transformers 라이브러리 설치해야 함
!pip install transformers
from transformers import pipeline

객체 탐지 실험

여러 개의 pipeline 실험 중 객체 탐지를 골라서 포스팅하게 된 이유는, 제일 재밌었기 때문 + Table Detection 프로젝트를 앞두고 객체 탐지와 조금 더 친숙해지고자 하는 목적입니다.

1. 크리스마스 사진

트랜스포머 객체탐지 기능은 이미지 속에 있는 객체를 탐지해서 score(확률), label(객체 명), box(위치 pixel) 3가지 결과값을 반환합니다. 어떤 사진을 고를까 고민하다가 저는 이 정신없는 크리스마스 사진을 고르게 되었습니다.

출처 : 나무위키

일부러 좀 정신없는 사진을 골라봤어요. 트랜스포머도 과연 저처럼 이 사진이 정신없다고 생각할까요? 그리고 과연 이렇게 정신없는 사진의 객체를 몇개나, 얼마나 정확하게 탐지할 수 있을까요?

import requests
from PIL import Image

# 이미지 데이터 가져오기
url = "https://i.namu.wiki/i/--GbZ0ptaE0KF8OgUej9I_SN4erfOc_ueyHgtJipMB0scNAJRSio6uWMcFviEGKO0d0qSqwWhla7xGfiB5NYoQgAPSmh8TQW1AAuYljDuveZiAwd8kcbOV4mFFpCVz6CMZ9cBBym3rPK19df_Blbhw.webp"
image_data = requests.get(url, stream=True).raw
image = Image.open(image_data)

# 이미지 확인하기
import matplotlib.pyplot as plt
plt.imshow(image)
plt.axis('off')
plt.show()

먼저 이미지 데이터를 불러오고 matplotlib을 통해 이미지를 확인해 보았습니다. 원하는 다른 이미지가 있으시면 image url을 대체해서 사용하시면 됩니다. 이미지는 어떻게 생겼는지 위에서 이미 보여드렸으니 결과값은 생략하고 넘어갈게요.

# 객체 탐지
from transformers import pipeline
detector = pipeline(task = 'object-detection') # 객체 탐지 task 설정
preds = detector(url) # 객체 탐지 실행
preds = [{"score": round(pred["score"], 4),  
          "label": pred["label"],
          "box": pred["box"]} for pred in preds] # 탐지 결과 보기좋게 차례대로 딕셔너리/리스트화

# 결과 확인
print(f"총 {len(preds)}개의 객체가 탐지되었습니다!")
for pred in preds:
	print(pred)

위와 같이 간단하게 pipeline을 이용해서 객체 탐지를 실행합니다. 사진 속에 여러개의 객체가 탐지될 경우 알아보기 쉽게 score, label, box 결과값을 하나의 딕셔너리로 묶어준 다음, 리스트화 합니다. for문을 사용해서 preds 안에 어떤 결과가 있는지 하나씩 프린트를 해봤는데요.

짜잔! 이렇게 총 12개의 객체를 탐지한 것을 확인할 수 있었습니다. label을 확인해 보니, 시계도 있고 고양이도 있고 말도 있고 식물도 있고... 이렇게 텍스트로 결과를 확인하면 직관적이지 못하니, 가지고 있는 이미지 위에 score, label, box값을 그려서 결과를 확인해 보겠습니다.

from matplotlib.patches import Rectangle

plt.figure(figsize = (15,8))
fig, ax = plt.subplots(1)
ax.imshow(image)
for pred in preds:
    box = pred["box"]
    label = pred["label"]
    score = pred["score"]
    xmin, ymin, xmax, ymax = box["xmin"], box["ymin"], box["xmax"], box["ymax"]
    width, height = xmax - xmin, ymax - ymin
    # 사각형 만들기
    rect = Rectangle((xmin, ymin), width, height, linewidth=2, edgecolor='r', facecolor='none')
    # 사각형 추가하기
    ax.add_patch(rect)
    # 레이블 추가하기
    plt.text(xmin, ymin, f'{label} ({score})', bbox=dict(facecolor='yellow', alpha=0.5))

plt.axis('off')
plt.show()

matplotlib의 patches중 Rectangle 기능을 이용해서 간단하게 사각형을 그려보았어요. 결과는 다음과 같습니다.

벽장에 있는 시계(저는 있는줄도 몰랐음), 구석에 있는 말(역시 눈에 안띄어서 잘 몰랐음), 뒤에 있는 사람, 아래 있는 고양이, 식물, 테이블, 꽃병... 정말 잘 감지한 것을 확인할 수 있었습니다. 기대 이상으로 성능이 좋아서 무척 놀라웠어요. 

preds = sorted(preds, key = lambda x: x['score'], reverse = True)[:5]

이 번엔 요렇게 preds 리스트를 score value값을 기준으로 내림차순 정렬해서 top 5개만 남기고 플롯을 그려봤습니다.

역시 고양이가 최고..! 0.9995의 score로 가장 높은 탐지 스코어를 기록했습니다.


2. 겨울왕국 포스터

이번엔 새로운 사진으로 바꿔봤는데요. 겨울왕국 포스터를 가져와 봤습니다. url만 갈아 끼우면 되니 코드는 이번에는 생략하고 결과만 바로 보여드리겠습니다.

아쉽게도 이번엔 크리스마스만큼 좋은 결과를 보이지 못했네요. 4개의 객체를 탐지했는데 대체적으로 score가 낮은 편입니다. 왼쪽 아래에 있는 괴물도 person으로 탐지를 했고, 오른쪽 아래에 있는 순록(?)은 아예 탐지가 되지 않았습니다. 

이렇게 예측 결과가 좋지 못한 경우를 발견했을 때 그 이유는 무엇인지, 비슷한 다른 사례로 일반화가 가능한지, fine-tuning하여 발전시킬 수 있을지를 생각해 보면서 프로젝트로 디벨롭해 나가면 좋겠다는 생각이 들었습니다. 


3. 치맥 사진

이번엔 아래 치맥 사진을 이용해서 객체탐지를 실행해 보겠습니다. 저는 가장궁금했던 것이,

  1. 여러 개의 치킨 조각을 하나로 '치킨'이라는 음식으로 탐지할 수 있을까?
  2. 잔에 담긴 액체를 보고 '맥주'라고 추론할 수 있을까?
  3. 큰 기대는 없긴 한데, 후라이드랑 양념치킨 구분은 안되겠지..? 였어요.

출처 : 나무위키에 치맥 검색

이쯤 되면 똑같은 코드를 계속 치기 번잡하니 함수화를 해줘야 국룰입니다. url을 넣으면 몇개의 객체가, 어떤 객체가 탐지되었는지 print를 하고, 결과를 그려 plot을 보여주는 함수로 만들어 보았습니다.

def object_detect_plot(url):
    image_data = requests.get(url, stream=True).raw # 이미지 데이터 가져오기
    image = Image.open(image_data)
    detector = pipeline(task = 'object-detection')
    preds = detector(url)
    preds = [{"score": round(pred["score"], 4), "label": pred["label"], "box": pred["box"]} for pred in preds]

    print(f"총 {len(preds)}개의 객체가 탐지되었습니다!")
    for pred in preds:
        print(pred)
    
    plt.figure(figsize = (15,8))
    fig, ax = plt.subplots(1)
    ax.imshow(image)
    for pred in preds:
        box = pred["box"]
        label = pred["label"]
        score = pred["score"]
        xmin, ymin, xmax, ymax = box["xmin"], box["ymin"], box["xmax"], box["ymax"]
        width, height = xmax - xmin, ymax - ymin
        # 사각형 만들기
        rect = Rectangle((xmin, ymin), width, height, linewidth=2, edgecolor='r', facecolor='none')
        # 사각형 추가하기
        ax.add_patch(rect)
        # 레이블 추가하기
        plt.text(xmin, ymin, f'{label} ({score})', bbox=dict(facecolor='yellow', alpha=0.5))

    plt.axis('off')
    plt.show()
chicken_n_beer = "https://i.namu.wiki/i/2JQMZZIxjIeZpag74qgmIQvBrS9gcBy-w_iTkHgQ34V8pS63SaWqUTgnMZGxJykuwBdXXPLUr6IRv7jCsLnQlVI-t6L37ZTo3CLlGIaCjDnnThCMtCzm4l1QjC2wLva-mkj4CqNtE716a1mERKcn5A.webp"
object_detect_plot(chicken_n_beer)

결과는 아래와 같습니다.

  1. 여러 개의 치킨 조각을 하나로 '치킨'이라는 음식으로 탐지할 수 있을까? -> 개별 치킨을 도넛으로 인식함(ㅋㅋㅋㅋ)
  2. 잔에 담긴 액체를 보고 '맥주'라고 추론할 수 있을까? -> 그냥 컵으로 인식함
  3. 큰 기대는 없긴 한데, 후라이드랑 양념치킨 구분은 안되겠지..? 였어요. -> 도넛부터 해결을 해야....
  4. + 다이닝 테이블에 대한집착...

네.. 생각보다 결과가 좋지 못했습니다. fried chicken이라는 label 자체가 없는 것으로 판단이 되는데, 하나의 치킨 조각을 도넛으로 인식을 하고, 여러 개의 치킨이 담겨 있는 전체를 하나의 음식 객체로 탐지하지도 못하는 것 같아요.


결론

이미지 객체 탐지 (Image Object detection)를 실행해 본 결과, 성능이 무척 좋긴 하지만 명확한 한계점이 여러 가지 보였습니다. 이러한 한계점은 파인튜닝 또는 새로운 SoTA 모델 연구를 통해서 극복할 수 있겠죠? 딥러닝 관련 프로젝트 주제를 선정할 때 이렇게 기존 SoTA 모델의 파이프라인으로 여러 가지 실험을 해보면서 프로젝트 방향을 설정한다면, 좋은 아이디어가 빠르게 도출될 수 있을 것 같습니다.

재미있었던 트랜스포머 파이프라인 가지고놀기 실험은 이것으로 마무리 하겠습니다 :-)

다음 포스팅에서는 트랜스포머 모델 PyTorch 구현을 해보도록 할게요. 감사합니다!

 

 

 

 

+ Recent posts