Gorio Tech Blog search

Python 프로젝트 생성하기

|

이번 글에서는 새로운 파이썬 프로젝트를 진행하기 위한 환경을 쉽고 빠르게 구축하는 방법에 대해 서술해보겠습니다.
본 글은 윈도우를 기준으로 설명합니다.


Python 프로젝트 생성하기

1. Poetry 설명

poetry는 의존성 관리와 빌드를 책임지는 라이브러리입니다.

poetry를 설치한 후, 아래 명령어를 입력합니다.

# 새로운 프로젝트를 만들 경우
poetry new my-project

# 기존 프로젝트를 활용할 경우
poetry init

이제 pyproject.toml 파일이 생성되었을 것입니다.

새로 가상 환경을 만든다고 할 때, 프로젝트의 root directory 아래에 virtualenv 가 있는 것이 편합니다. 만약 새로 만드는 것이 싫다면 아래와 같이 설정합니다.

poetry config virtualenvs.create false # 기본 값은 true

이 링크를 참조하셔도 좋습니다.

프로젝트 내부에 .venv 폴더를 생성하는 옵션은 아래와 같습니다.

poetry config virtualenvs.in-project true # 기본 값은 None

의존성은 위 파일의 [tool.poetry.dependencies][tool.poetry.dev-dependencies]에서 관리하고 있습니다. add 서브 커맨드를 통해 의존성을 추가할 수 있습니다.

poetry add numpy

이 때 poetry.lock 파일이 생성됩니다.

다음 명령어를 실행하면 전체적으로 업데이트가 가능합니다.

poetry update

현재 프로젝트의 pyproject.toml 파일을 읽어 의존성 패키지를 실행하고 싶을 때는 아래 명령어를 실행합니다.

poetry install

설치된 패키지 목록은 show를 통해 알아 볼 수 있습니다.

# 설치된 모든 패키지
poetry show

# 개발환경용은 제외
poetry show --no-dev

# 세부 내용
poetry show django

# 최신 버전
poetry show --latest (-l)

# 업데이트가 필요한 패키지
poetry show --outdate (-o)

# 의존성 트리
poetry show --tree

가상 환경에 대한 정보는 아래 명령어로 확인할 수 있습니다.

# 가상 환경 정보 확인
poetry env info

# 가상환경 리스트 확인
poetry env list

2. Github Action 설명

Github Action은 github에서 제공하는 CI/CD 도구이며, 소프트웨어 개발의 workflow를 자동화해줍니다.

Github Action에는 반드시 알아야 할 개념들이 있습니다. 이 문서를 확인하는 것이 가장 정확합니다.

가장 기본적인 설명은 이러합니다.

PR 생성과 같이 repository에 특정 event가 발생하면 트리거되는 Github Action workflow를 구성할 수 있습니다. workflow는 순차적 혹은 병렬적으로 동작하는 1개 이상의 job을 갖고 있습니다. 각각의 job은 할당된 가상 머신 runner 내부 혹은 container 내부에서 동작하며 action 혹은 script를 실행하도록 되어 있습니다.

workflow

  • 1개 이상의 job을 실행시키는 자동화된 프로세스
  • yaml 파일로 정의함
  • repository 내에 .github/workflows 디렉토리 안에서 정의됨
  • 하나의 repository는 복수의 workflow를 정의할 수 있음

events

  • workflow run을 트리거하는 특정 행동

jobs

  • 동일한 runner 내에서 실행되는 여러 step
  • 각 step은 shell script이거나 동작할 action

action

  • workflow의 가장 작은 블록
  • 재사용이 가능한 component

runner

  • github action runner 어플리케이션이 설치된 머신
  • workflow가 실행될 인스턴스

action 개념에 대해 추가 설명이 필요하다면 이 블로그를 참고하셔도 좋겠습니다.

이제 실제로 workflow를 작성해 보겠습니다. 앞서 소개한 여기를 참고해주세요. workflow 구문에 대해 자세히 알고 싶다면 여기를 참고하면 됩니다.

workflow는 yaml 파일에 기반하여 구성됩니다. name은 workflow의 이름을 의미하며, 이 name이 repository의 action 페이지에 표시될 것입니다. on은 workflow가 작동하도록 트리거하는 event를 정의합니다. 대표적으로 push, pull_request 등을 생각해 볼 수 있을 텐데, 모든 조건에 대해 알고 싶다면 여기를 확인해 주세요.

특정 event의 경우 filter가 필요한 경우가 있습니다. 예를 들어 push가 어떤 branch에 발생했는지에 따라 트리거하고 싶을 수도 있고 아닐 수도 있습니다. 공식 문서의 설명은 여기에 있습니다.

지금까지 설명한 내용의 예시는 아래와 같습니다.

name: CI

on:
  pull_request:
    branches: [main]

이제 job을 정의해 보겠습니다. 복수의 job을 정의할 경우 기본적으로 병렬 동작하게 됩니다. 따라서 순차적으로 실행되길 원한다면 반드시 jobs.<job_id>.needs 키워드를 통해 의존 관계를 정의해야 합니다. 각 jobruns-on으로 명시된 runner environment에서 실행됩니다. environment에 대해서는 여기를 확인하면 됩니다.

아래 예시에서 my_first_job과 my_second_job이 job_id에 해당한다는 점을 알아두세요. My first job과 My second job은 jobs.<job_id>.name (name) 이며 github UI에 표시됩니다.

jobs:
  my_first_job:
    name: My first job
  my_second_job:
    name: My second job

needs를 통해 반드시 이전 job이 성공적으로 끝나야만 다음 job이 실행되도록 정의할 수 있습니다. 물론 조건을 추가해서 꼭 성공하지 않더라도 실행되도록 작성할 수도 있습니다.

jobs:
  job1:
  job2:
    needs: job1
  job3:
    needs: [job1, job2]

앞서 job 내에는 연속된 task로 구성된 steps가 존재한다고 설명했습니다. jobs.<job_id>.steps는 명령어를 실행하거나 setup task를 수행하거나 특정한 action을 실행할 수 있습니다.

steps 아래에 if 조건을 추가하게 되면 반드시 이전의 조건이 만족되어야 연속적으로 실행되도록 만들 수 있습니다. context를 이용한다면 아래 예시를 보면 됩니다.

steps:
 - name: My first step
   if: $
   run: echo This event is a pull request that had an assignee removed.

status check function을 이용해 보겠습니다. My backup step이라는 task는 오직 이전 task가 실패해야만 실행될 것입니다.

steps:
  - name: My first step
    uses: octo-org/action-name@main
  - name: My backup step
    if: $
    uses: actions/heroku@1.0.0

steps의 구성 요소를 좀 더 살펴보겠습니다. jobs.<job_id>.steps[*].name은 github UI에 표시될 name을 의미합니다.

jobs.<job_id>.steps[*].uses는 어떠한 action을 실행할지를 의미합니다. 이는 같은 repository나 public repository 혹은 published docker container image에서 정의될 수 있습니다.

다양한 예시가 존재합니다. versioned action, public action, public action in a subdirectory, same repository 내의 action 및 docker hub action 등을 이용할 수 있습니다. 이에 대한 공식 문서 설명은 여기를 확인해 주세요.

여러 action 중 가장 많이 사용되는 action은 checkout인데, repository로부터 코드를 다운로드 받기 위해 사용됩니다. 이 checkout을 github action의 입장에서 바라보면 github의 repository에 올려 둔 코드를 CI 서버로 내려받은 후 특정 branch로 전환하는 작업으로 이해할 수 있다고 합니다. (참고 블로그 인용) 실제로 이 action을 직접 수행하려면 번거로운 작업들이 선행되어야 하지만, github action은 이를 편하게 묶어서 action으로 제공하고 있습니다.

workflow yaml 파일에서 steps.uses 키워드에 사용하고자 하는 action의 위치를 {소유자}/{저장소명}@참조자 형태로 명시해야 한다고 합니다. 예시는 아래와 같습니다.

steps:
  - name: Checkout
      uses: actions/checkout@v3

내부적으로는 git init/config/fetch/checkout/log 등의 명령어를 수행한다고 합니다. 가장 최신 버전의 checkout action에 대해서는 이 repository에서 확인할 수 있습니다.

python으로 개발을 하는 사용자라면 파이썬을 설치하는 setup-python 또한 자주 사용하게 될 것입니다. repo는 여기입니다. 이 action을 통해 CI 서버에 python을 설치할 수 있으며 특정 버전이 필요할 경우 아래와 같이 작성하면 됩니다.

steps:
    - name: Set up Python
        uses: actions/setup-python@v3
        with:
            python-version: 3.9

jobs.<job_id>.steps[*].run은 운영체제의 shell을 이용하여 command-line 프로그램을 실행시킨다. 아래와 같이 single-line command를 입력할 수도 있고,

- name: Install Dependencies
  run: npm install

multi-line command로 입력할 수도 있습니다.

- name: Clean install dependencies and build
  run: |
    npm ci
    npm run build

예시는 여기를 참고해 주세요. 파이썬 스크립트 예시 하나는 기록해 둡니다.

steps:
  - name: Display the path
    run: |
      import os
      print(os.environ['PATH'])
    shell: python

jobs.<job_id>.steps[*].with는 action에 의해 정의된 input 파라미터들의 map을 의밓바니다. 각 input 파라미터는 key/valu 쌍으로 이루어져있으며, input 파라미터들은 환경 변수들의 집합에 해당합니다.

그 외에도 steps 아래에는 args, entrypoint, env 등 여러 속성을 정의할 수 있습니다.


3. 정리

이번 chapter에서는 위 내용과 더불어 publishing까지 전체 flow 진행에 대해 알아보겠습니다.

pycharm을 이용한다면 다음과 같이 처음부터 poetry를 이용해서 편리하게 프로젝트를 시작할 수 있습니다.

이전에 init 을 통해 생성되었던 poetry.lockpyproject.toml 파일이 생성되었을 것입니다.

사용하는 library 들을 poetry add {library} 를 통해 추가해줍니다.

이제 github action을 이용하여 CI/CD 환경을 구축해줄 차례입니다. 아래와 같이 .github 디렉토리 아래에 2개의 yaml 파일을 생성해 줍니다.

먼저 CI 부분부터 살펴보겠습니다. 아래는 ci.yaml 파일의 예시입니다.

name: CI

on:
  pull_request:
    branches: [main]

jobs:
  ci-test:
    name: ci-test
    runs-on: windows-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Set up Python
        uses: actions/setup-python@v3
        with:
          python-version: 3.9
      - name: Install dev-dependencies
        run: pip3 install -r requirements-dev.txt
      - name: Lint
        run: make check

조건은 간단합니다. main branch에 PR이 생성되었을 경우 make check 명령문을 실행하게 됩니다. 이 부분은 아래와 같이 작성하였습니다.
(pylint, isort, black 모두 poetry add를 통해 의존성 추가를 해주어야 합니다.)

.PHONY: init format check requirements

init:
		pip install poetry
		poetry install

format:
		isort --profile black -l 119 {library} lint.py
		black -S -l 119 {library} lint.py

check:
		isort --check-only --profile black -l 119 {library}
		black -S -l 119 --check {library}

requirements:
		poetry export -f requirements.txt -o requirements.txt --without-hashes
		poetry export --dev -f requirements.txt -o requirements-dev.txt --without-hashes

참고로 Windows 환경에서 Makefile을 작성하기 위한 방법은 이 곳에 잘 설명되어 있습니다.

위 파일 작성을 마쳤다면, PR을 생성하기 이전에 코드 정리가 잘 되어 있는지, requirements 파일은 생성했는지 확인해 주면 됩니다.

PR이 closed 되었을 때, CD 구조가 진행되도록 해보겠습니다. 아래는 publish.yaml 파일의 예시입니다.

name: PUBLISH LIBRARY

on:
  pull_request:
    branches: [main]
    types: [closed]

jobs:
  build:
    name: build-library
    runs-on: windows-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Set up Python
        uses: actions/setup-python@v3
        with:
          python-version: 3.9
      - name: Install requirements
        run: pip3 install poetry
      - name: Bump version
        run: poetry version patch
      - name: Publish library
        env:
          PYPI_TOKEN: $
        run: |
          poetry build
          poetry publish --username $ --password $  

Install requirements 까지는 추가적인 설명이 필요하지 않아 보입니다.

Bump version은 패키징하고자 하는 library의 version을 자동적으로 upgrade하는 부분입니다. library를 0.1.0에서 0.1.1로 올릴지, 0.2.0으로 올릴지, 1.0.0으로 올릴지 선택할 수 있습니다.
이 곳을 참고하면 자세한 정보를 확인할 수 있습니다.

다음은 여러 token을 읽고 build -> publish까지 이어지는 부분입니다. 일단 secrets가 무엇인지부터 확인해 보겠습니다. github repository의 Actions Secrets은 환경 변수를 암호화해서 저장할 수 있는 기능입니다. Settings-Security-Secrets를 통해 접근할 수 있습니다.

PYPI 홈페이지에서 본인의 repository에서 사용할 token을 추가해줍니다. 그리고 생성한 값을 복사한 뒤, PYPI_TOKEN secrets에 저장해줍니다. 마찬가지로 PYPI_USERNAMEPYPI_PASSWORD도 추가해줍니다.

이렇게 추가된 token들은 인증과정에 사용됩니다.

이제 CI/CD 구축은 끝났습니다. library 코드를 정리하고, PR 과정을 정상적으로 거치면 프로젝트 생성부터 패키징, 배포까지 편리하게 진행할 수 있습니다.

References

Comment  Read more

Resnet 계열 image classification 모델 설명

|

이번 글에서는 Resnet을 기반으로 한 여러 image classification 네트워크 들에 대해 정리해보겠습니다.

그 대상은 아래와 같습니다.

본 글에서는 핵심적인 부분에 대해서만 살펴보겠습니다.


ResNeXt 설명

ResNext에서는 cardinality라고 하는 개념이 등장합니다. transformation 작업의 크기 혹은 종류라고 생각하면 되는데, 이 hyper-parameter만 잘 조절해도 depth나 channel을 크게 증가시키지 않으면서도 성능 향상을 이끌어 낸다고 합니다.

왼쪽은 ResNet에 등장했던 bottleneck layer의 구조입니다. 오른쪽은 ResNeXt에서 제안된 구조인데, cardinality를 32개로 설정, 즉 path를 32개로 만든 뒤, 이를 average 하여 병합하는 것을 알 수 있습니다. shortcut 구조는 동일합니다.

ResNet-50과 ResNeXt-50 with 32x4d 구조를 보면, parameter 수나 FLOPs의 경우 유사함을 알 수 있습니다.

실제로 같은 연산이지만 표현 방식은 아래와 같이 다양합니다.


ResNeSt 설명

1. 핵심 내용

feature map attention과 multi-path representation이 visual recognition에서 중요하다는 사실은 잘 알려진 사실입니다. ResNeXt에서는 다른 network branch에 대하여 channel-wise attention을 적용함으로써 cross-feature interaction을 포착하고 다양한 representation을 학습하는 데에 있어 효과적인 방법론을 제시합니다.

위에서 소개하였던 ResNeXt에서 cardinality (K) hyperparameter를 이야기 하였는데요, 이 값은 곧 featuremap group의 수를 의미합니다. 본 논문에서는 이 featuremap groupcardinal group이라고 부릅니다. 그리고 이 cardinal group을 또 나눈 (split) 수를 의미하는 $R$ = radix hyper-parameter 라는 개념을 추가합니다. 즉 모든 feature group의 수는 아래와 같이 표현할 수 있습니다.

[G = K R]

  • $G$ = feature group 총 수
  • $K$ = # cardinality
  • $R$ = # splits within cardinal group

즉, input feature의 채널이 $C$ 개 있다고 할 때 이를 $K$ 개의 cardinality group으로 나누고, 이를 다시 $R$ 개로 split 하는 것입니다.

$k$ 번째 cardinality group의 representation은 아래와 같이 표현됩니다.

[\hat{U}^k = \Sigma_{j = R(k-1) + 1}^{RK} U_j]

[k \in 1, 2, .,,, K]

[\hat{U}^k \in \mathbb{R}^{H, W, C/K}]

k=1 일 때, j=1~R
k=2 일 때, j=R+1 ~ 2R 이 됩니다.

위 그림의 split-attention 과정까지 합쳐서 shape이 변화하는 과정을 나타내면 아래와 같습니다.

이렇게 구해진 $s_c^k$ 는 일종의 attention score의 역할을 수행하게 되고, 최종적으로 cardinal group representation의 가중 결합은 channel-wise soft attention을 통해 이루어지게 됩니다.

[V_c^k = \Sigma_{i=1}^R a_i^k(c) U_{R(k-1)} + i]

지금까지 설명한 것은 사실 cardinality-major 구현 방식인데, 실제로 이 방식으로 표준 CNN을 구성하는 것은 기술적으로 어렵습니다. 따라서 실제 코드로 구현해서 사용할 때는 radix-major 구현 방식을 이용한다고 합니다.

자세한 학습 방식과 실험 결과는 논문을 참조하길 바랍니다. 몇 가지만 메모를 하자면,

  • 네트워크의 마지막 2단계에서 DropBlock layer를 사용했습니다.
  • conv, fc layer에만 weight decay를 적용하였습니다.
  • 본 네트워크는 ResNet-D에 기반하였는데, 몇 가지 layer 구성 방식이 다릅니다. 자세한 사항은 논문 5페이지를 참조하면 됩니다.
  • auto augmentation, mixup, large crop 등의 기법을 통해 성능을 향상시켰습니다.

Res2Net 설명


ReXNet 설명

Comment  Read more

STAR benchmark 논문 설명(STAR - A Benchmark for Situated Reasoning in Real-World Videos)

|

이 글에서는 MIT 등 연구자들이 STAR benchmark 논문을 간략하게 정리한다.


STAR: A Benchmark for Situated Reasoning in Real-World Videos

논문 링크: STAR: A Benchmark for Situated Reasoning in Real-World Videos

Repo: http://star.csail.mit.edu/#repo
Github: https://github.com/csbobby/STAR_Benchmark

  • NIPS 2021
  • Bo Wu(MIT-IBM) et al.

Abstract

  • 주변의 상황으로부터 지식을 얻고 그에 따라 추론하는 것은 매우 중요하고 또한 도전적인 과제이다.
  • 이 논문에서는 실세계 영상에 대해 situation abstraction, logic-grounded 질답을 통해 situated 추론 능력을 평가하는 새로운 benchmark를 제시한다.
  • STAR(Situated Reasoning in Real-World Videos)
    • 이는 사람의 행동, 상호작용 등과 연관된 실세계 영상에 기반하여 만들어진 것으로 naturally dynamic, compositional, logical한 특성을 가진다.
    • 4가지 형태의 질문(interaction, sequence, prediction, and feasibility)을 포함한다.
    • 이 실세계 영상의 situations은 추출한 단위 entity와 relation을 연결한 hyper-graph로 구성된다.
    • 질문과 답변은 절차적으로 생성되었다.
  • 여러 영상 추론 모델을 이 데이터셋에 적용하여 보았을 때 상황 추론 task에서 어려움을 겪는 것을 발견하였다.
  • Diagnostic neuro-symbolic 모델을 제시하며, 이 benchmark의 challenge를 이해하기 위한 이 모델은 visual perception, situation abstraction, language understanding, and functional reasoning을 disentangle할 수 있다.

1. Introduction

그림 1과 같은 (실세계) 상황에서 우리(사람)는 어떻게 행동할지, 현실적인 결정을 무의식적으로 내릴 수 있다. 그러나 기계한테는 주어진 문맥과 상황을 모두 고려하여 결정을 내린다는 것은 꽤 어려운 문제이다.

  • 상황을 formulae의 집합으로 두고 가능한 logic 규칙을 만들어 이해하려는 시도가 있었으나 모든 가능한 logic rule을 만드는 것은 불가능하며 현실성이 떨어진다.

현존하는 비디오 이해 모델들을 도전적인 task에서 테스트한 결과 성능이 매우 낮아짐을 확인하였다. 이 모델들은 추론 자체보다는 시각적인 내용과 질답 간 연관성을 leverage하는 데에 집중하고 있었다.

이 논문에서는 STAR benchmark를 제안한다.

  • 4종류의 질문을 포함한다: interaction question, sequence question, prediction question, and feasibility question.
    • 각 질문은 다양한 장면과 장소에서 얻어진 action 중심 situation과 연관되어 있으며 각 situation은 여러 action과 연관되어 있다.
  • 현존하는 지식과 상황에 따라 유동적으로 변화하는 지식을 표현하기 위해 entity와 relation으로 구조화된 표현으로 추상화하였다(situation hypergraphs).
  • 시각적 추론 능력에 집중하기 위해 (자연어) 질문은 간결한 형태의 template에 맞춰 생성되었다.
  • 보조 용도로, (더욱 어려운) 사람이 만든 질문을 포함하는 STAR-Humans도 같이 제공한다.
  • 다른 데이터셋과 비교한 결과는 Table 1에 있다.

또한, Neuro-Symbolic Situated Reasoning (NS-SR)라는, 실세계 situated 추론을 위한 neural-symbolic 구조를 갖는 diagnostic model을 제안한다. 이는 질문에 답변하기 위해 구조화된 situation graph와 situation으로부터 얻은 dynamic clues를 활용한다.

이 논문이 기여한 바는,

  • interaction, sequence, prediction, and feasibility questions에 집중하여, 실세계 영상에서 situated reasoning 문제를 형식화했다.
  • situated reasoning을 위해 잘 설계된 benchmark인 STAR을 구성하였다.
    • 3가지 측면(visual perception, situation abstraction and logic reasoning)에서 annotation이 설계되었다.
    • 각 영상은 situation hyper-graph로 grounded되어 있으며 각 질문은 functional program으로 연관되어 있다.
  • 여러 SOTA 방법을 STAR로 테스트해 보았고 ‘사람에게는 자명한 상황’에서 모델은 실수가 많음을 보였다.
  • Diagnostic neuro-symbolic framework을 설계하였고 더욱 강력한 추론 모델을 위한 연구 방향을 제시하였다.

  • Visual Question Answering
  • Visual Reasoning

모델의 추론 능력을 진단하기 위한 여러 데이터셋이 있다: CLEVR, GQA, MarioQA, COG, CATER, CLEVRER, etc.

  • Situation Formalism

3. Situated Reasoning Benchmark

situations abstraction과 logical reasoning을 결합하였고, 아래 3가지 가이드라인을 따라 benchmark를 구축했다.

  1. 추상화를 위한 bottom-up anotations에 기반한 계층적 graph로 표현되는 situations
  2. situated reasoning을 위한 질문과 선택지 생성은 정형화된 질문, functional programs, 공통 situation data types에 grouded됨
  3. situated reasoning이 situation graphs에 대해 반복적으로 수행할 수 있음

만들어진 데이터셋의 metadata는 다음과 같다.

  • 60K개의 situated reasoning 질의
  • 240K개의 선택지
  • 22K개의 trimmed situation video clip으로 구성된다.
  • 144K개의 situation hypergraph(structured situation abstraction)
  • 111 action predicates
  • 28 objects
  • 24 relationships
  • train/val/test = 6:1:1
  • 더 자세한 내용은 부록 2, 3 참조

3.1. Situation Abstraction

Situations

Situation은 STAR의 핵심 컨셉으로 entity, event, moment, environment를 기술한다. Charades dataset으로부터 얻은 action annotation과 9K개의 영상으로 situation을 만들었다. 영상들은 주방, 거실, 침실과 같은 11종류의 실내환경에서의 일상생활이나 활동을 묘사한다. 각 action별로 영상을 나눌 수 있으며 영상 역시 이에 맞춰서 나눌 수 있다.

각 action은 (1) action precondition과 (2) effect로 나눌 수 있다.

  1. action precondition은 환경의 초기 static scene을 보여주기 위한 첫 frame이다.
  2. action effect는 하나 또는 여러 개의 action의 process를 기술한다.

질문의 종류에 따라서는:

  • interaction, sequence 타입의 question은 완전한 action segment를 포함한다.
  • prediction, feasibility 타입의 question은 불완전한 action segment를 포함하거나 아예 포함하지 않는다.

Situation Hypergraph

Situation video를 잘 표현하기 위해, situation의 추상적인 표현을 얻기 위해 hypergraph 형태의 실세계 situation에서의 dynamic process를 기술한다. hypergraph는 action과 그 관계, 계층적 구조를 포함한다. 그림 1에서와 같이 각 situation video는 person 및 object node와, 한 frame 내의 person-object 또는 object-object 관계를 표현하는 edge를 포함하는 subgraph 여러 개로 구성된다. 한편 각 actino hyperedge는 여러 subgraph를 연결한다. 몇몇 경우에 여서 action이 겹치며 subgraph 안의 node들이 서로 공유된다.

수식으로 나타내면, $H = (X, E)$ : situation hypergraph $H$는 situation frame에 나타나는 person 또는 object를 나타내는 node들의 집합 $X$와 action들에 대한 subgraphs $S_i$의 hyperedge들의 공집합이 아닌 집합 $E$로 구성된다.

다른 spatio-temporal graph와 달리 action을 frame 수준 subgraph 대신 hyperedge로 나타낸다.

situation hypergraph의 annotation 작업은 다음과 같다:

  • action temporal duration과 나타난 object들에 대한 annotation에 기반하여 one-to-many connection을 action hyperedge로 생성한다.
  • action annotation은 Charades에서 얻었으며 person-object relationships(Rel1), objects/persons annotation은 ActionGenome에서 얻었다.
  • object-object relationships(Rel2)는 detector VCTree(with TDE)를 사용하여 추출하였다.
  • Rel1과 Rel2에 더하여 person-object relations(Rel3)을 추가하였다.
    • 예를 들어 <person, on, chair><chair, on the left of, table>이라는 관계가 존재하면, <person, on the left of, table> 또한 존재한다.
  • 모든 모델은 video를 입력으로 사용하지만, hypergraph annotation(entities, relationships, actions, or entire graphs)는 더 나은 visual perception이나 structured abstraction을 학습하는데 사용될 수 있다.

3.2. Questions and Answers Designing

QA 엔진은 모든 질문, 답변, 옵션들을 situation hypergraph에 기반하여 생성한다.

Question Generation

situation reasoning에서 여러 난이도를 다루고 각각 다른 목적을 가지는 여러 타입의 질문을 설계하였다.

  • Interaction Question (What did a person do …): 주어진 상황에서 사람과 물체 간 상호작용을 이해하는 기본 테스트이다.
  • Sequence Question (What did the person do before/after …): dynamic situation에서 연속적인 action이 있을 때 시간적 관계를 추론하는 능력을 측정한다.
  • Prediction Question ( What will the person do next with…): 현 상황에서 다음 행동으로 타당한 것을 예측하는 능력을 측정한다. 주어진 상황은 action의 첫 1/4만큼만 보여지며 질문은 나머지 action이나 결과에 대한 것이다.
  • Feasibility Question (What is the person able to do/Which object is possible to be …): 특정 상황 조건에서 실현 가능한 action을 추론하는 능력을 평가한다. 상황 통제를 위해 spatial/temporal prompt를 사용한다.

일관성을 위해 모든 질문은 잘 설계된 template과 hypergraph의 data로부터 생성되었다. [P], [O], [V], [R]로 각각 person, objects, action verbs, relationships을 나타내고 생성 과정은 다음과 같다:

  • situation annotations과 hypergraphs로부터 데이터를 추출한다.
  • 추출한 데이터로 question template을 채운다.
  • 어구 조합(phrase collocation)이나 형태론(morphology) 등으로 확장한다.

Answer Generation

STAR hypergraph에 기반한 functional program으로 정확한 답을 자동으로 만든다. Suppl. figure 5에서 자세한 과정을 볼 수 있다.

Distractor Generation

정말 추론을 잘하는지 아니면 단순 확률만 추정하는 건지 확인하기 위한 작업으로 다음 3가지 distractor 전략을 설계했다.

  1. Compositional Option: 주어진 상황과 반대되는 옵션을 주는 가장 어려운 옵션이다. 또한 잘 어울리는 verb-object이며(합성성compositionality를 만족) 같은 상황에서 일어나는 사실을 기반으로 만든다.
    • Random Option: 이 옵션 역시 합성성을 만족하지만 다른 임의의 situation hypergraph에서 선택된다.
    • Frequent Option: 모델을 확률로 속이는 옵션인데 각 질문 그룹에서 가장 자주 일어나는 옵션을 고른다.

모든 옵션은 각 질문에 대해 랜덤 순서로 배치된다.

Debiasing and Balancing Strategies

옷을 입는다문손잡이를 잡는다같이 자주 나타나는 단어쌍 등이 있는데 모델은 이를 통해 영상을 보지도 않고 답을 맞출 수도 있다. 이런 것을 막기 위해 다른 여러 단어들과 좋바될 수 있는 동사나 명사를 선택하여 남겨두었다. 즉 편향 제거 작업이다.

Grammar Correctness and Correlation

문법 체크기를 사용해서 정확성을 87%에서 98%로 올렸다.

Rationality and Consistency

생성된 situation video, 질문, 선택지의 품질과 상관성을 확보하기 위해 AMT를 사용, rationality와 consistency를 확보한 데이터만 남겼다.


4. Baseline Evaluation

4지선다형 문제에 정답을 맞추는 비율로 여러 모델을 테스트했다.
사용한 모델들은 아래 표와 같다. Q-type random과 frequent는 각각 완전 임의선택, 가장 자주 나타나는 답만 선택하는 모델이다.
Blind Model은 언어 정보만 가지고 추론하는 모델이다.

4.1. Comparison Analysis

위 표 결과에서 보듯이 STAR는 질문이 여러 타입을 가져 꽤 어려운 문제임을 알 수 있다. object 상호작용이나(LGCN) 더 나은 visual representation을 얻을 수 있는 HCRN, ClipBERT 등의 모델이 성능이 좀 낫다.


5. Diagnostic Model Evaluation

neuro-symbolic framework인 Neuro-Symbolic Situated Reasoning(NS-SR)을 diagnostic model로 제안한다.

5.1. Model Design

Video Parser

사람 또는 물체 중심 상호작용 정보를 얻는 detector로 구성된다.

  • object는 Faster R-CNN, X101-FPN
  • relationships는 VCTree with TDE-sum, GloVe
  • pose는 AlphaPose
  • action은 MoViNets

를 사용했다.

Transformers-based Action Transition Model

  • Situation Hypergraph Encoder: 추출한 entity나 relation을 연결하여 “초기” situation hypergraph를 생성한다.
  • Dynamics Transformer Model: dynamic하게 action state나 relationship을 예측하도록 설계되었다. transformer block은 VisualBERT로 구현하였다.
  • Graph Sequence Decoder

Language Parser

attention 기반 Seq2Seq 모델로 질문을 대응되는 program으로 parsing한다. 데이터셋의 질문이 single-select이므로 질문 및 선택지를 각각 parsing하는 2개의 모델을 사용했다. 각각 bidirectional LSTM encoder와 LSTM decoder로 구성된다.

Program Executor

hypergraph 위에서 program을 실행하는 Program Executor를 설계했다. 질문을 parsing한 program을 그대로 hypergraph 위에서 따라가며 추론하는 방식이라고 생각하면 된다.

5.2. Result Analysis

완벽한 GT를 가지고 program을 수행하면 당연하게도 100% 정답률을 보인다. (데이터셋이 그렇게 만들어졌으므로)

아래는 각 요소가 없는 경우를 분석한 것이다.

Situation Abstraction: 완벽한 시각적 인식과 추론 논리를 가지지만 완벽한 구조화된 상황 추상화 능력이 없어 성능이 많이 떨어진다. Visual Perception: 시각적 정보를 처리하는 것이 중요함을 나타낸다. 성능이 더 떨어졌다. Language Understanding: 완벽한 프로그램을 사용하지 않는 경우 성능은 1% 이내로 약간 감소한다. 이는 STAR의 언어 인식이 어렵지 않다는 것을 의미한다. Without Ground-Truths: 이 결과는 그리 좋지 못한데 추가 연구가 많이 이루어질 수 있다. visual perception과 situation abstraction을 개선하는 데 초점을 맞춰야 한다.


6. Conclusion

  • 실제 상황에서의 추론을 위해 새로운 벤치마크 STAR를 도입하여 그에 맞는 추론 방법을 모색하였다.
  • 지각 외에도 상향식 상황 추상화와 논리적 추론을 통합합니다.
    • 상황 추상화는 동적 상황에 대한 통합적이고 구조화된 추상화를 제공하며, 논리적 추론은 정렬된 질문, 프로그램 및 데이터 유형을 채택합니다.
  • 시스템이 동적 상황에서 학습하고 특정 상황에서 네 가지 유형의 질문(상호작용, 순서, 예측, 실현 가능성)에 대한 합리적인 답을 찾아야 하는 상황 추론 과제를 설계하였다.
  • 실험을 통해 상황적 추론이 최첨단 방법으로는 여전히 어려운 과제임을 입증하였다.
  • 또한 neuro-symbolic architecture로 새로운 진단 모델을 설계하여 상황적 추론을 탐구하였다.
  • 상황 추론 메커니즘이 완전히 개발되지는 않았지만, 이 결과는 벤치마크의 도전과제를 보여주고 향후 유망한 방향을 제시하였다.
  • STAR 벤치마크가 실제 상황 추론에 대한 새로운 기회를 열어줄 것으로 믿는다고 한다.

Comment  Read more

Python glob, os, platform, shutil 사용법(Python os 다루기)

|

이 글에서는 Python 라이브러리인 os와 platform에 대해 알아본다. 가끔 쓰는데 막상 쓰려면 언제 봐도 헷갈리는 라이브러리 중 하나인 듯 하다.


Import

import glob
import os
import platform

glob

특정 경로에 존재하는 파일 or 디렉토리의 리스트를 불러온다.

현재 디렉토리의 구조가 다음과 같다고 하자.

current
├── .gitignore
├── readme.md
├── data
|   ├── 1.jpg
|   ├── 2.jpg
|   ├── 3.png
|   ├── 4.png
├── model
|   ├── faster_rcnn_r50_1.pth
|   ├── faster_rcnn_r50_2.pth

아주 중요한 문자가 있다.

  • *: 임의의 문자열과 매치된다.
  • /: 특정 디렉토리를 경로로 지정할 때 사용한다.
  • **: 재귀적으로 디렉토리 전체를 탐색할 때 사용한다.
  • ?: 하나의 임의의 문자와 매치된다.
  • []: 대괄호 내의 문자들 중 하나와 매치된다.

자세한 설명은 정규표현식을 살펴보면 된다.

사용법은 아래 예시를 보면 쉽게 알 수 있다.

glob.glob

print(glob.glob('data/*.jpg')
# ['data/1.jpg', 'data/2.jpg']
print(glob.glob('./data/*.jpg')
# ['./data/1.jpg', './data/2.jpg']

glob.glob('./[0-9].*')처럼 사용할 수도 있다. 파일 이름이 숫자 한개인 파일들과 매치된다.

glob으로 가져오는 경로는 사용자가 지정한 디렉토리 경로 전체도 같이 가져오므로 “파일명”만 갖고 싶을 때에는 .split() 함수 등으로 쪼개서 가져와야 한다.
윈도우에서는 / 대신 \\\로 표시될 수 있다.

glob.iglob

glob.glob 함수와 거의 동일한데 리스트가 아닌 반복가능한(iterable) 형태로만 나온다. 파일이 매우 많다면 이쪽을 쓰는 것이 메모리 등에서 좋다.

for filename in glob.glob('data/*.png'):
    print(filename)
    
# data/3.png
# data/4.png

glob.glob(‘*/.jpg’, recursive=True)

glob은 기본적으로 특정 디렉토리 하나의 파일만을 탐색한다. 그래서 재귀적으로 디렉토리 내부에 존재하는 모든 디렉토리들을 포함해서 탐색하고 싶으면 ** 문자와 recursive 옵션을 써야 한다.

for filename in glob.glob('**/*', recursive=True):
    print(filename)

# data
# model
# readme.md
# data/1.jpg
# data/2.jpg
# data/3.png
# data/4.png
# model/faster_rcnn_r50_1.pth
# model/faster_rcnn_r50_2.pth

특정 확장자만 모든 디렉토리 내에서 찾고 싶다면 glob.glob('**/*.png', recursive=True)와 같이 쓰면 된다.

glob.escape

모든 특수 문자(?, * [)를 이스케이프 처리한다. glob과 비슷하지만 특수문자가 들어있는 파일명을 처리하고 싶을 때 사용한다.


os

working directory

os.getcwd()

현재 working directory 경로를 반환한다.

print(os.getcwd())

# result
'C:\\Users\\gorio\\Documents\\current'

os.chdir()

working directory를 변경한다.

os.chdir('./data)'

파일 탐색

os.walk(path)

파일이나 디렉토리의 목록을 iterable 형태로 얼을 수 있다.

# 현재 디렉토리 안에 있는 모든 파일 탐색
for curDir, dirs, files in os.walk('.'):
     for f in files:
        print(os.path.join(curDir, f))


# result
./.gitignore
./readme.md
./data/1.jpg
./data/2.jpg
./data/3.png
./data/4.png
./model/faster_rcnn_r50_1.pth
./model/faster_rcnn_r50_2.pth

os.listdir()

지정한 디렉토리의 하위 파일 및 디렉토리만 반환한다. 디렉토리 내부의 파일은 탐색하지 않는다.

os.listdir('.')

# result
['.gitignore', 'data', 'model', 'readme.md']

os.path

파일이나 디렉토리의 존재를 확인하거나 경로를 얻거나, 경로와 파일명을 결합하는 등의 함수를 포함한다.

os.path.isfile('readme.md')
os.path.split('./data/1.jpg')
os.path.splitext('./data/1.jpg')
# result
True
('./data', '1.jpg')
('./data/1', '.jpg')
함수명 설명
basename(path) path에서 앞의 디렉토리 경로는 다 떼고 파일명만 반환
dirname(path) path에서 파일명은 떼고 디렉토리 경로만 반환
isdir(path) 주어진 경로가 디렉토리이면 True를 반환
isfile(path) 주어진 경로가 파일이면 True를 반환
split(path) 주어진 경로를 디렉토리 경로와 파일명으로 분리한 tuple 반환
splitext(path) 주어진 경로를 확장자와 나머지로 분리한 (경로, 확장자) tuple 반환

shutil

shutil.copy(src, dst, *, follow_symlinks=True)

src 경로의 파일을 dst 경로로 복사한다. dst가 디렉토리 경로이면 해당 디렉토리에 파일을 복사하고, 파일명까지 포함되어 있으면 해당 파일명으로 변경하며 복사한다.

shutil.copy('1.jpg', 'data/')

참고로,

  • follow_symlinks가 False이고 src가 심볼릭 링크이면, dst는 심볼릭 링크로 만들어진다.
  • follow_symlinks가 True이고 src가 심볼릭 링크이면, dstsrc가 참조하는 파일의 사본으로 만들어진다.

shutil.copy2(src, dst, *, follow_symlinks=True)

파일 메타 데이터를 보존한다는 것을 빼면 shutil.copy와 동일하다.

shutil.copyfile(src, dst, *, follow_symlinks=True)

dst는 디렉토리로 지정할 수 없다. 이외에는 사실상 동일하다.

shutil.move(src, dst, copy_function=copy2)

파일이나 디렉토리를 (재귀적으로) 이동시킨다.

shutil.copytree

shutil.copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, ignore_dangling_symlinks=False, dirs_exist_ok=False)

특정 경로 하위에 있는 모든 파일와 디렉토리를 원하는 위치에 복사할 수 있다.

from distutils.dir_util import copy_tree
copy_tree("./test1", "./test2")

shutil.rmtree(path, ignore_errors=False, onerror=None)

전체 디렉토리 tree를 삭제한다.

실질적으로 잘 안쓰지만 이런 함수들도 있다.

  • shutil.copymode: src의 권한 bit를 dst로 복사한다. linux에서 w,r 등의 옵션을 가리킨다.
  • shutil.copystat: 권한 bit, 마지막 액세스 시간, 마지막 수정 시간 및 flag를 복사한다.
  • shutil.disk_usage(path): 지정한 경로의 디스크 사용량 통계를 (total, used, free) attribute를 갖는 named tuple로 반환한다.

platform

시스템 정보를 확인하는 모듈이다.

기본적으로 값을 판별할 수 없으면 빈 문자열을 반환한다.

platform.system()

현재 구동되고 있는 OS(Operating System)의 종류를 알려준다.

platform.system()

# result
'Windows'

기타 플랫폼 관련 함수

platform.node()

네트워크 이름 또는 사용자명을 반환한다.

platform.node()

# result
'gorio'

platform.release()

릴리즈 번호를 반환한다.

platform.release()

# result
'10'

platform.machine()

기계 유형을 반환한다. 어디선가 본 듯한 amd64, i386 등이 반환된다.

platform.machine()

# result
'AMD64'

python 버전 관련

예시만 보면 바로 사용법을 알 수 있다.

>>> platform.python_implementation()
'CPython'
>>> platform.python_version()
'3.9.10'
>>> platform.python_version_tuple()
('3', '9', '10')

>>> platform.python_build()
('tags/v3.9.10:f2f3f53', 'Jan 17 2022 15:14:21')
>>> platform.python_compiler()
'MSC v.1929 64 bit (AMD64)'
>>> platform.python_branch()
'tags/v3.9.10'

References

Comment  Read more

Tab-Transformer(Tabular Data Modeling using contextual embeddings) 설명

|

이번 글에서는 tabular 데이터에 대해 Transformer를 적용하여 새로운 알고리즘을 고안한 논문에 대해 다뤄보겠습니다.

논문 원본은 이 곳에서 확인할 수 있습니다.

공식 repository는 awslabs-autogluon-tabtransformer를 확인하시면 되는데, 그대로 사용하기에는 어려운 부분이 있을 것입니다. 따라서 아래 두 깃헙도 참고하면 좋습니다.

본 글에서는 핵심적인 부분에 대해서만 살펴보겠습니다.


Tab-Transformer(Tabular Data Modeling using contextual embeddings) 설명

1. Introduction

tabular 데이터에 대해 tree-based 모델이 뛰어난 성능을 보이는 것은 사실입니다만, 한계점 또한 분명히 존재합니다. tabular 데이터와 image/text를 한 번에 학습시키는 multi-modality를 확보할 수 없고, 스트리밍 데이터에 대해 지속적인 학습 또한 불가능한 측면이 있습니다.

단순한 MLP로 임베딩을 한다고 하면, 그 얕은 구조와 context-free 임베딩이라는 특성 때문에 성능 측면에서 아쉬운 부분이 많습니다.

본 논문에서는 tree-based 모델에 필적하면서도 MLP보다 뛰어난 구조의 알고리즘을 소개합니다.

2. TabTransformer

전체적인 구조 자체는 어렵지 않기 때문에 컨셉만 잘 이해하면 됩니다. DLRM 처럼 연속형 변수와 범주형 변수의 처리 방법 자체가 아예 다릅니다. 연속형 변수는 layer normalization를 거치고 난 후 최종 layer로 바로 투입되는 형태이지만, 범주형 변수의 경우 Column Embedding 과정을 거친 후 Transformer 구조를 통과 한 후에 최종 layer로 투입됩니다.

Column Embedding에 대해 상술해 보겠습니다. 범주형 변수가 $m$ 개가 존재한다고 할 때, 각각의 변수에는 또한 여러 class가 존재할 것입니다. 일단 Column Embedding을 통과하게 되면, $m$ 개의 범주형 변수는 $m$ 개의 임베딩 벡터로 변환됩니다. 만약 길이가 $d$ 라고 한다면, 길이 $d$ 를 갖는 $m$ 개의 벡터를 갖게 될 것입니다.

$i$ 번째 범주형 변수가 $d_i$ 개의 class를 갖고 있다고 하면, 임베딩 테이블 $e_{\phi_i} (.)$ 는 $d_i + 1$ 개의 임베딩을 갖게 됩니다. 1개가 추가된 것은 결측값에 대응하기 위함입니다. 해당 범주형 변수에 결측값이 많은 경우 이렇게 별도의 임베딩을 생성하면 되고, 만약 충분하지 않다고 하면 다른 임베딩 벡터의 평균 값 등을 이용할 수도 있을 것입니다.

모든 범주형 변수의 각 class에 대해 독립적인 임베딩 벡터를 만들 수도 있지만, 각 범주형 변수는 분명 다른 특성을 갖게 됩니다. 예를 들어 성별, 직업 이란 2개의 범주형 변수가 있다고 하면, 남성/여성이라는 특성은 분명 직업과는 다른 종류의 의미를 갖고 있을 것입니다. 이 때문에 같은 변수 내에서 일부 같은 parameter를 공유하게 설정할 수 있습니다. $i$ 변수의 $j$ class에 대한 변환을 식으로 표현하면 아래와 같습니다.

[e_{\phi_i} (j) = [\mathbf{c}{\phi_i}, \mathbf{w}{\phi_{ij}}]]

[\mathbf{c}{\phi_i} \in \mathbb{R^l}, \mathbf{w}{\phi_{ij}} \in \mathbb{R^{d-l}}]

이 때 $\mathbf{c}$ 라고 하는 각 변수 내에 존재하는 공유되는 parameter를 어느 정도 비중으로 가져갈 지는 실험의 영역입니다. 즉 $l$ 은 hyper-parameter에 해당합니다. 적정한 $l$ 을 찾는 것은 실험으로 해결해야하는 부분입니다만 논문의 ablation study에서 그 힌트를 찾을 수 있습니다. 논문에서는 1/4 또는 1/8이 가장 적절한 비율이라고 판단하였습니다.

tabular 데이터에서는 변수 간 순서라는 것이 존재하지 않는 경우가 많기 때문에, positional encoding을 쓰는 대신 이런 식으로 다른 중요한 정보를 활용할 수 있습니다.

앞서 설명하였듯이 이렇게 Column Embedding을 통해 생성된 벡터는 Transformer layer를 통과하게 됩니다. 통과한 결과물은 아래와 같이 표현할 수 있습니다.

[[\mathbf{h}_1, \mathbf{h}_2, …, \mathbf{h}_m]]

이 벡터들을 Contextualized Embedding이라고 부르며, top MLP에 투입되기 전 연속형 변수인 $\mathbf{x}_{cont}$ 와 합쳐지게 됩니다. 그렇다면 이 concatenated 벡터의 차원은 $(d*m + c)$ 입니다.

top MLP를 거치면 최종 output이 산출되게 됩니다.

3. Experiments

실험 종류가 많아서 모두 자세히 기록하지는 않겠습니다. 몇 가지만 살펴보겠습니다.

기본적인 MLP와의 성능 비교는 아래와 같습니다.

noisy 데이터와 결측값이 있는 데이터에 대해서도 TabTransformer는 기본적인 MLP 보다 더 높은 성능을 보여줍니다. (robust)

지도 학습 상의 모델 성능을 보면, TabTransformer는 GBDT에 필적하는 성능을 보임을 알 수 있습니다.

부록에 보면 Column Embedding에서 $\mathbf{c}_{\phi_i}$ 의 비율에 대한 실험이 나옵니다. Transformer Layer의 수에 따라 조금씩 다르지만 보통 1/4 ~ 1/8의 비율이 높은 성능을 보여줌을 알 수 있습니다. Column Embedding이 아예 없는 경우가 제일 좋지 않은 성능을 보여준 것도 확인해 보아야 할 대목입니다.

4. Conclusion

semi-supervised learning에 대한 이야기도 많이 있으니 (본 글에서는 생략) 논문을 직접 참고하시길 바랍니다. TabTransformer는 tabular 데이터를 이용한 딥러닝 알고리즘으로 MLP, GBDT에 비해 차별화된 장점을 갖고 있는 방법론입니다. 다양한 유형의 데이터를 소화할 수 있으면서도 안정적인 성능을 낼 수 있는 알고리즘으로 평가할 수 있겠습니다.

Comment  Read more