태그 보관물: python

Datumaro를 이용한 dataset format 확인

Datumaro는 Intel에서 Opensource로 배포하는 Dataset 관리를 위한 프레임 워크이다. Dataset 간의 변환 등의 다양한 기능을 제공하는데, 이를 이용하면 주어진 dataset이 어떤 포맷인지도 쉽게 알 수 있기에 유용하다. 이 포스팅에서는 Datumaro를 설정하고 dataset의 포맷을 확인하는 방법을 알아본다.

Dependencies

이 포스팅에서는 datumaro에 포함된 datum의 dataset download 기능을 이용해서 다양한 형식의 dataset을 download 받을 수 있었는데, 이를 이용하려면 다음의 의존성이 만족해야 한다.

pip install 'datumaro[tf,tfds]' \
   tensorboardX \
   tensorflow-dataset \
   importlib_resources \
   nibabel

tfds는 TensorFlow project에서 관리하는 dataset 컬렉션이고 여기에서 곧바로 다운로드를 받아 올 수 있다. 문서 상에는 Kaggle도 지원하는 것처럼 되어 있기는 한데, 실제로는 다운로드 되지 않았다(Jul 2026 현재).

Dataset Download

의존성 설치가 완료된 다음에는 datum 명령어를 이용해서 다운로드 가능한 dataset의 목록을 볼 수 있다.

datum download tfds describe --report-format json

위의 예제 명령줄은 tfds로 부터 목록을 받아오는 것이며 전술 했듯, 현재 시점에서 kaggle은 아무 내용도 보여 주지 않는다.

출력되는 결과에는 dataset의 이름과 그것에 대한 세부 사항들이 표시되는데, 특정한 dataset을 받기 위해서는 datum downloadget 명령어를 사용하면 된다. 예를 들어 다운로드 받은 항목이 다음과 같이 표시되었다고 하면,

{
  "tfds:cifar10": {
    "default_output_format": "cifar",
    "description": "The CIFAR-10 dataset consists of 60000 32x32 colour images in 10 classes, with 6000 images per class. There are 50000 training images and 10000 test images.",
    "download_size": 170052171,
    "home_url": "https://www.tensorflow.org/datasets/catalog/cifar10",
    "human_name": "CIFAR-10",
    "num_classes": 10,
    "version": "3.0.2",
    "subsets": {
      "train": {
        "num_items": 50000
      },
      "test": {
        "num_items": 10000
      }
    }
  },
...
}

이 데이터셋 “tfds:cifar10”을 다운로드 받기 위해 dataset의 id를 명시해서 다음의 명령어를 수행하면 된다.

mkdir -p ~/Downloads/dataset
datum download tfds get -i tfds:cifar10 -o ~/Downloads/dataset/cifar10

Dataset format detection 예제

특정한 dataset의 형식을 알고자 한다면 datumaro.Dataset.detect() method의 path parameter에 dataset의 root directory를 넘겨주면 어떤 형식인지 str로 반환해 준다.

내 경우, dataset을 압축 포맷으로 저장해 두는 경우가 많아서, 압축파일을 임시 디렉토리에 풀고 해당 dataset의 format을 검사해서 출력하도록 작성했다.

실행결과

현재 다운로드가 가능한 tfds의 모든 format들과 내가 가지고 있던 dataset들의 format을 잘 detect 해 주었다. 다만, voc_2012 dataset은 하나 이상의 format으로 매치 되는 MultipleFormatsMatchError가 올려지고 있어서 이 경우에 대한 부가적인 처리가 필요할 것 같다. 예제 에서는 그냥 중복 매칭된 포맷들의 목록을 출력해 주었다.

Rust와 Python unit test의 공존

예전 글에서 vscode의 test explorer에 Rust의 unittest가 보이도록 설정하는 방법을 다룬 적이 있었는데, 여기에 Python test case (여기서는 pytest)도 함께 표시되도록 하려면 .vscode/settings.json을 편집해 주어야 한다.

해당 프로젝트의 경우 가상환경을 source root에 두지 않고 서브 디렉토리인 service/.venv 안에 넣어 두었기 때문에 python.defaultInterpreterPath 값을 이곳으로 직접 설정해 주고 pytest 사용을 위한 설정도 해 두었다.

{
    // Rust unit test
    "rust-analyzer.testExplorer": true,

    // Python virtual environment용 인터프리터 설정
    "python.defaultInterpreterPath": "${workspaceFolder}/service/.venv/bin/python", 
    
    // pytest 사용
    "python.testing.pytestEnabled": true,
    "python.testing.unittestEnabled": false,

    // Python unit test가 있는 디렉토리 경로
    "python.testing.pytestArgs": [
        "service/test"
    ],
    "python-envs.defaultEnvManager": "ms-python.python:venv"
}

그리고 나서 vscode GUI에서도 다시 한번 venv를 설정해 준다. 그냥 프로그램을 재 실행 했으면 이 부분은 건너 뛰어도 되었을 것 같긴 한데, 오류가 계속 뜨길래 수동으로 설정해 주었다.

이렇게 하고 나면 test explorer에 두 언어의 test case들이 모두 표시되는 평화로운 공존상태가 된다.

Expectimax Algorithm과 간단한 예제

Expectimax는 동전 던지기나 카드 뒤집기 혹은 주사위 게임 처럼 “운”(확률)이 개입되는 게임에서 컴퓨터 프로그램으로 어떤 선택을 해야 최적의 결정을 내릴 수 있을지에 대해 정의하는 알고리즘이다.

서로 경쟁하는 두 플레이어의 합리적 결정을 가정하는 Minimax algorithm과 달리 이 문제에서는 확률적 요소가 있기 때문에, 내가 얻을 점수를 최대화하는 선택을 하기 위한 Max 노드와 더불어 발생할 수 있는 모든 시나리오의 기대값을 계산하는 Chance 노드가 등장한다.

Chance 노드의 기댓값은 다음의 수식으로 계산된다.

V(S)=iP(Si)V(Si)V(S) = \sum_i P(S_i) \cdot V(S_i)

여기에서 V(S)는 Chance 노드에서의 기대값이고 , P(Si)는 어떤 사건 i가 발생할 확률, V(Si)는 그 사건이 발생 했을 때의 얻게 되는 가치를 의미한다. 즉 발생확률과 이익의 곱을 모두 더한 것이다.

Max와 Chance

Max가 하는 일은 minimax와 동일하게 자신에게 최대한의 이익이 되는 선택을 하는 것이다. 반면 Chance 노드의 경우는 각 노드가 발생할 확률과 그 사건이 발생했을 때의 이익으로 계산된다.

사건이 발생할 확률이 0.5로 동일하고 그 결과값이 각각 10, 2, 6, 5인 아래와 같은 tree가 있다고 하자.

각 Chance node의 선택 값은 다음과 같이 계산된다.

왼쪽 노드(L)의 값

(10×0.5)+(2×0.5)=6(10 \times 0.5) + (2 \times 0.5) = 6

오른쪽 노드(R)의 값

(6×0.5)+(5×0.5)=5.5(6 \times 0.5) + (5 \times 0.5) = 5.5

이에 따라 Max는 6점인 왼쪽(L) 노드를 선택하게 된다.

주사위 게임 예제

주사위를 던져서 나온 눈의 수 만큼 점수를 가져가는 단순한 게임이 있다고 해보자. 규칙은 다음과 같다.

  • 주사위 게임규칙
    • 번갈아 가며 주사위를 던진다.
    • 주사위는 멈추고 싶을 때까지 원하는 만큼 던질 수 있다.
    • 주사위를 던저서 나오는 눈의 수 만큼이 자기 점수에 합산된다.
    • 다만, 눈이 1이 나오면 지금까지의 모든 점수를 다 잃고 0점이 된다.

이 게임에서 해결하고자 하는 문제는 컴퓨터가 주사위를 더 던질지 아니면 멈출지에 대한 결정이다. 이 결정을 위해 Expectimax 알고리즘을 적용해 보자.

현재까지 획득한 점수가 5점이라고 가정한다면, 각 선택 tree에 대한 chance node계산은 다음과 같다.

  • 선택 1 – 그만 던지기: 최종 획득 5점
  • 선택 2 – 던지기
    • 1이 나올 확률 1/6: 최종 획득 0점
    • 2 ~ 6이 나올 확률 5/6: 최종 획득 7.5점
16×0+16×(7+8+9+10+11)=7.5\frac{1}{6} \times 0 + \frac{1}{6} \times (7 + 8 + 9 + 10 + 11) = 7.5

주사위를 던지지 않으면 획득가치는 5점, 던지면 7.5점이므로 Expectimax 알고리즘은 주사위를 던지는 결정을 선택 한다.

그렇다면 현재 점수가 30점일 때는 어떨까?

  • 선택 1 – 그만 던지기: 최종 획득 30점
  • 선택 2 – 던지기
    • 1이 나올 확률 1/5: 최종 획득 0점
    • 2 ~ 6이 나올 확률 5/6: 최종 획득 28.33
16×0+16×(32+33+34+35+36)28.33\frac{1}{6} \times 0 + \frac{1}{6} \times {(32+33+34+35+36)} \approx 28.33

주사위를 던지지 않으면 획득가치는 30점, 던지면 28.33점이므로 Expectimax 알고리즘은 이번에는 주사위를 던지지 않는 결정을 선택 한다.

파이썬 코드 구현

Conclusion

이상으로 단순한 주사위 게임을 예로들어 Expectimax를 살펴 보았다. 확률이 개입되는 BlackJack이나 2048게임의 solver 같은 것을 구현 할 때에도 Expectimax는 어떤 결정을 해야할 것인 가에 대한 합리적인 해답을 제시해 주는 기본 토대가 되어 줄 수 있을 것이다. 보다 복잡한 문제를 푸는데 실제 적용을 위해서는 다양한 heuristic들이 보다 정교하게 고려되어야 하기는 하겠지만 상대가 두는 최악의 수만 고려하는 Minimax와 달리 확률적 환경의 무작위 성을 ‘개댓값’이라는 계산 가능한 값으로 받아들이는 Expectimax는 불확실성이 개입되는 많은 현실의 문제를 해결하는데 있어서 강력한 모델링 도구가 되어 줄 수 있을 것이다.