3 분 소요

Hail의 기초

Hail을 처음 해보는 입문자에게는 Hail에서 제공하는 Tutorial을 따라 해보는 것이 좋은 방법이 될 수 있습니다. Hail의 Tutorials 링크입니다. 웹 뿐만 아니라 Jupyter notebook 파일 형식으로도 Tutorial을 제공하기 때문에 직접 돌려보면서 Hail에 대해서 익혀보시면 좋을 것 같습니다.

1) hail.init 함수

가장 기초적인 부분인 Hail을 시작하는 방법부터 배워보도록 하겠습니다. Hail은 Python만으로 작동하지 않습니다. 대부분이 Java/Scala 언어로 프로그래밍되어 있고 Java 가상머신(JVM)과 함께 구동됩니다. Hail은 module 내에 함수를 실행하면 자동적으로 Spark와 함꼐 시작하도록 세팅되어 있습니다. 그러나 자동적으로 시작하는 경우 Hail에서 기본값으로 지정한 설정으로 시작되기 때문에 Spark의 환경변수를 설정하거나 기본적으로 사용할 Reference Genome를 설정하고 싶다면 hail.init 함수을 호출하여 Hail을 수동으로 시작하면 좋습니다.

이 함수 중 쓸만한 parameter를 한번 보도록 할까요?

hail.init(sc=None, app_name='Hail', master=None, local='local[*]',
          log=None, quiet=False, append=False, min_block_size=0,
          branching_factor=50, tmp_dir=None, default_reference='GRCh37',
          idempotent=False, global_seed=6348563392232659379,
          spark_conf=None, skip_logging_configuration=False,
          local_tmpdir=None, _optimizer_iterations=None)

sc는 SparkContext를 의미하는데 Spark에서 꼭 필요한 개념이지만 Hail만을 가지고 스크립트를 짜게 된다면 기본적으로 Hail에 의해서 SparkContext가 생성되고 작업 내내 유지되기 때문에 넘어가겠습니다.

master는 여러 대의 서버를 클러스터링했을 때 연결된 모든 서버를 관리하는 클러스터 매니저의 URL를 인수로 입력하는 parameter입니다. 기본값은 None으로 되어 있는데 그대로 두면 local 모드, 즉 서버 한 대만 사용하게 됩니다. 여러 종류의 클러스터 매니저(standalone, yarn, mesos 등) 중에서 하나를 선택하여 입력하면 됩니다. 저 같은 경우는 standalone 모드로 클러스터를 구성하였는데 인수로 master='spark://{server_ip}:7077'를 넣어서 사용하고 있습니다. cluster 모드를 설정하는 방법은 나중에 자세하게 다뤄보겠습니다.

localmaster를 설정하지 않았을 때 parameter로 사용되는데 서버의 core를 얼마나 사용할지 설정합니다. 기본값은 local[*]으로 * 표시는 CPU의 모든 core를 사용하겠다는 것을 의미합니다. core 수를 특정하려면 [, ] 사이에 core 수를 넣으면 됩니다. e.g. local='local[20]' –> core 20개 사용

log는 사용자가 직접 log 파일의 이름과 위치를 설정하는 parameter입니다. 기본적으로는 실행하는 스크립트 파일이 있는 폴더에 log 파일이 저장됩니다. 이렇게 되면 log 파일이 정돈되지 않고 관리하기 어렵기 때문에 저는 따로 log 파일을 저장하는 폴더를 만들고 그 안에 log 파일을 저장하고 있습니다. e.g. log='path/to/log_dir/hail_20210703_1.log'

default_reference는 기본적으로 사용할 reference genome을 설정합니다. tlocus라는 genomics 데이터를 다루기 위한 특이한 Hail의 data type이 존재하는데, reference genome를 설정하는 부분이 있습니다. 따로 설정을 해두지 않으면 default_reference에서 정의된 값이 반영됩니다. default_reference의 기본값은 GRCh37인데, reference genome으로 GRCh37(hg19)보다는 GRCh38(hg38)을 많이 사용하신다면 default_reference='GRCh38'으로 설정해두시면 편합니다.

spark_conf는 Spark의 환경 설정에 넣을 설정 값들을 dict 형식으로 넣으면 됩니다. Spark의 Driver, Executor에 할당할 메모리 또는 코어 수를 설정하거나, Spark와 관련된 다른 option을 설정할 수도 있습니다. spark_conf에 사용되는 속성들은 여기에서 볼 수 있습니다. 예를 들어, Spark의 기본 설정대로 큰 데이터를 다루다 보면 꼭 다음과 같은 오류 메세지로 memory overhead가 생기는데요. 이 때는 Spark의 Driver나 Executor의 메모리 용량을 올려주셔야 합니다.

Hail version: 0.2.70
Error summary: OutOfMemoryError: GC overhead limit exceeded

보통 서버 한 대만 사용하는 local 모드에서는 Driver가 Executor 역할을 같이 수행하기 때문에 Driver의 메모리 설정만 해주시면 overhead 문제를 해결하실 수 있습니다. e.g. spark_conf={'spark.driver.memory':'100g'}

참고로, 제가 클러스터 모드(Standalone)에서 기본으로 사용하는 hl.init 설정을 남겨둡니다.

config = {
    'spark.driver.memory' : '100g',
    'spark.executor.memory' : '200g'
}
hl.init(
    spark_conf = config,
    log = 'path/to/log_dir/hail' + date + '.log',
    default_reference = 'GRCh38',
    master = 'spark://{master_ip}:7077'
    )

2) Expression

jupyter notebook을 통해 Hail의 함수 코드를 한 줄씩 실행하다 보면 시간이 걸리는 코드도 있고 바로 넘어가는 코드도 존재합니다. 그 이유가 무엇일까요? 결과 값을 불러오는 Action 연산을 해야 이전까지 제출된 Transformation 연산들을 한꺼번에 수행하는 Spark의 특성 때문인데요, 효율을 증가시키는 장점이 있다고 합니다. 바로 넘어가는 코드는 Transformation 연산이 되겠고 시간이 걸리는 코드는 Action이라고 생각하면 되겠네요.

Hail의 data type은 고유의 Expression class를 가지고 있습니다. 예를 들어, LocusExpression의 경우 tlocus 타입의 expression이 되겠네요. Hail은 expression을 이용하여 연산을 수행할 수 있습니다. 다음은 두 expression을 이용하여 새로운 expression을 만드는 과정입니다.

>>> import hail as hl
>>> x = hl.int32(5)
>>> y = hl.int32(6)
>>> z = x + y
>>> z
<Int32Expression of type int32>

하지만 이때 z는 연산이 수행된 상태가 아닙니다! 아직 z의 결과를 호출하지 않았기 때문이죠. z에 대해서 Action 연산을 하는 경우는 다음과 같습니다. eval는 expression을 Python 값으로 바꾸는 함수이고, show는 expression의 값을 우리가 볼 수 있게 해주는 함수입니다. 바로 이때 z에 대한 Transformation 연산까지 같이 수행됩니다.

>>> hl.eval(z)
11
>>> z.show()
+--------+
| <expr> |
+--------+
|  int32 |
+--------+
|     11 |
+--------+

expression의 또 다른 특징은 expression끼리의 연산 이외에 Python value도 함께 연산이 된다는 점입니다. 따라서, 두 개의 코드 라인은 같은 결과를 보여줍니다.

>>> hl.eval(hl.int32(5) * 6)
30
>>> hl.eval(hl.int32(5) * hl.int32(6))
30

Hail의 expression는 처음에는 생소하고 어려운 개념이지만, Hail Table 또는 Matrix Table을 다루다 보면 자연스럽게 익힐 수 있을 것입니다. 다음 시간에는 Hail Table과 Matrix Table을 읽는 방법부터 변환하여 쓰는 방법까지 본격적으로 다뤄보겠습니다.

Reference

댓글남기기