답사기 · 제8장 정독

돌을 옮기지 않고
탁본을 뜬다

금석문을 조사하는 사람은 비석을 뽑아 오지 않는다. 종이를 대고 먹을 두들겨 글자만 떠 온다. 제8장의 기법이 그렇다. 원격 파일을 내 컴퓨터로 내려받지 않고 그 자리에 두고 묻는다. 그리고 놀라운 것은, 필요한 글자 몇 줄만 떠 오는 부분 탁본이 가능하다는 사실이다.

원전
DuckDB: Up and Running
지은이
Wei-Meng Lee
펴낸곳
O'Reilly Media, 2024
대상
Chapter 8. Accessing Remote Data Using DuckDB
얼마나 떠 오는가 · HTTP RANGE ① CSV · SELECT * 파일 전부 · 행 기반이므로 쪼갤 수 없다 ② Parquet · SELECT * 파일 전부 · 모든 열을 달라 했으므로 ③ Parquet · 두 열 Agency · "Agency Type" 두 열만 ④ Parquet · avg(age) age 한 열만 ⑤ Parquet · count(*) 아무것도 · 메타데이터만 읽는다 질의문 한 줄이 통신량을 정한다. 파일은 그 자리에 그대로 있다. HF:// 경로의 네 토막 hf://datasets {user} {dataset} {file} 고정된 머리 사용자 이름 데이터셋 이름 파일 · 폴더가 있으면 앞에 붙인다 실례 셋 hf://datasets/scikit-learn/tips/tips.csv hf://datasets/AiresPucrs/adult-census-income/ data/train-00000-of-00001-7e70ed54d8cbb057.parquet hf://datasets/gabrielwu/city_country/*.csv 비공개 데이터셋에는 열쇠가 하나 더 붙는다 CONFIG · 토큰을 코드에 적는다 CREDENTIAL_CHAIN · 파일에서 찾는다 CREATE SECRET hf_token ( TYPE HUGGINGFACE … 문법은 하나, 열쇠를 어디에 두는가만 갈린다.

답사의 눈금. 제1장이 기둥의 장, 제2장이 문의 장, 제3장이 칸의 장, 제4장이 때의 장, 제5장이 자리의 장, 제6장이 겹의 장, 제7장이 야장의 장이었다면 제8장은 탁본의 장이다. 왼쪽 다섯 단계는 같은 원격 파일을 두고 질의문만 바꾼 결과이며, 눈금은 실제로 통신선을 타고 오는 양이다. 마지막 단계에서는 데이터가 한 바이트도 오지 않는다. 비문의 글자 수를 세는 데 굳이 종이를 대지 않아도 되는 것처럼, 그 수는 이미 머릿돌에 적혀 있다.

지금까지는 모두 가까운 데이터였다

DUCKDB'S HTTPFS EXTENSION

앞선 모든 장에서 DuckDB로 다룬 것은 지역 데이터였다. MySQL 데이터베이스에 있든 CSV·JSON·Parquet 파일에 있든 모두 내 컴퓨터의 것이었다. 그러나 실제 상황에서 다루는 데이터는 통상 원격 서버에 놓이며, 여러 곳에서 오는 일이 잦다. 다행히 DuckDB는 원격 데이터셋에 접근하게 해 주는 httpfs 확장을 제공한다. 게다가 DuckDB는 Hugging Face가 호스팅하는 데이터셋에 접근하는 지원까지 제공한다.

httpfs 확장은 자동 적재 가능한(autoloadable) 확장이며, 원격 파일의 읽기와 쓰기를 허용하는 파일 시스템을 구현한다. 이 확장으로 DuckDB는 HTTP와 HTTPS 프로토콜을 통해 파일을 직접 읽고 쓸 수 있으며, 먼저 지역에 내려받을 필요가 없다. 다음 상황에서 특히 도움이 된다.

  • 용량을 넘길 때 지역 저장 공간을 넘어서는 큰 데이터셋을 다룰 때.
  • 실시간일 때 실시간이거나 자주 갱신되는 데이터에 접근할 때.
  • 여러 곳일 때 여러 원격 원천에 분산된 데이터에 질의할 때.
  • 클라우드일 때 클라우드 저장과 매끄럽게 통합할 때.

이는 효율적인 원격 데이터 분석을 가능하게 하며, 클라우드 기반 데이터 레이크, 웹 API, 분산 파일 시스템이 얽힌 상황에 이상적이다. httpfs 확장은 CSV, Parquet을 비롯해 DuckDB가 자체적으로 지원하는 여러 파일 형식을 지원한다.

도움말 — S3

httpfs 확장은 Amazon S3(Simple Storage Service) API를 써서 객체 저장에 대한 읽기와 쓰기, 그리고 파일 글로빙도 지원한다.

Python + SQL세션당 한 번
import duckdb

conn = duckdb.connect()
conn.execute('''
    INSTALL httpfs;
    LOAD httpfs;
''')
httpfs 확장의 설치와 적재는 DuckDB 세션마다 한 번만 하면 된다

제2장에서 엑셀을 읽기 위해 spatial을, MySQL에 붙기 위해 mysql을 설치했던 그 자리에 이번에는 httpfs가 놓인다. DuckDB의 확장 목록을 처음 소개한 대목에서 httpfs가 맨 앞에 있었던 것을 떠올려도 좋다. 여섯 장을 지나 그 첫 항목의 차례가 온 셈이다.

검토 방법에 관한 밝힘

이 장은 결과가 스크린숏 도판이고, 절차의 절반이 Hugging Face 웹사이트를 누르는 순서다. 그래서 이 페이지는 다음을 지켰다. 도판의 표 값은 옮기지 않고, URL·명령·절차의 순서를 정확히 옮기는 데 힘을 쏟았다. URL은 원서 지면에서 대부분 오른쪽이 잘려 있으므로, 다른 곳의 인쇄분과 대조해 복원할 수 있는 것만 복원했다.

표기 규칙은 앞 일곱 장과 같다. 복원한 부분은 점선 밑줄, 복원하지 않은 부분은 다. 이 권의 구조색은 금석(金石)에 낀 청동 녹빛, 동록(銅綠)이다.

원격 CSV — 결국 전부 온다

ACCESSING CSV FILES

httpfs 확장이 있으면 원격에 놓인 파일에 접근할 수 있다. 웹 서버에 파일이 있다면 읽거나 쓰려는 파일을 곧바로 가리키는 URL을 그냥 쓰면 되고, DuckDB가 먼저 지역에 내려받지 않고 처리한다. 그런데 GitHub 같은 사이트에 저장된 파일이라면 원시 파일(raw file)을 담은 URL을 얻어야 한다.

표본 CSV 파일의 URL을 웹 브라우저에 띄우고 Raw 버튼을 누른다. 원시 CSV 파일을 적재하는 페이지로 옮겨지며, 그 페이지의 URL을 복사한다. 이 예에서는 Amazon.com의 과거 주가를 담은 파일이다.

원시 파일의 URL도판 8-1
https://raw.githubusercontent.com/weimenglee/DuckDB_Book/main/AMZN.csv
도판 8-1 · GitHub에서 원시 파일의 URL을 얻는다. DuckDB는 이 URL로 질의를 수행한다
SQL원격 CSV 질의와 여과
# 파일 전체를 질의해 pandas DataFrame으로 바꾼다
conn.execute('''
  SELECT
    *
  FROM
    'https://raw.githubusercontent.com/weimenglee/DuckDB_Book/main/AMZN.csv'
''').df()

# 원격 데이터에 여과도 걸 수 있다 — 2018년의 모든 행
conn.execute('''
  SELECT
    *
  FROM
    'https://raw.githubusercontent.com/weimenglee/DuckDB_Book/main/AMZN.csv'
  WHERE year(Date) = 2018;
''').df()
도판 8-2 · 8-3 · 원격 CSV 파일이 내려와 DataFrame이 되고, 2018년의 행만 골라낸다

여러 CSV 파일을 한꺼번에 읽으려면 read_csv() 함수에 목록을 넘긴다.

SQL두 파일을 잇는다
conn.execute('''
  SELECT
    *
  FROM read_csv([
    'https://raw.githubusercontent.com/weimenglee
    'https://raw.githubusercontent.com/weimenglee
  ]);
''').df()
도판 8-4 · GitHub에서 두 CSV 파일을 적재해 내용을 이어 붙인다
주의 — 스키마가 다르면 붙지 않는다

두 CSV 파일의 스키마가 다르면 내용이 행 단위로 이어 붙지 않는다. 제6장에서 여러 JSON 파일을 합칠 때 키 이름으로 열을 맞추고 빈 자리를 NaN·None으로 채웠던 그 관용은 여기에 없다.

그리고 이 절의 결론이 뜻밖이다. CSV 파일은 대부분의 경우 지역 기계로 전부 내려온다. CSV가 데이터를 행 기반 형식으로 저장하기 때문이다. 큰 파일을 다룰 때 특히 시간이 걸린다. 원격 데이터를 훨씬 효율적으로 가져오는 길은 Parquet 형식을 쓰는 것이다.

제1장의 첫 그림이 여기서 다시 필요해진다. 행 기반 저장에서는 한 행을 읽으려면 그 행의 모든 열이 함께 온다. 이제 그 파일이 통신선 저쪽에 있다면, 같은 사정이 대역폭의 문제로 바뀐다. 기둥으로 세워 두지 않은 데이터는 원격에서도 쪼갤 수 없다.

원격 Parquet — 필요한 기둥만 뜬다

ACCESSING PARQUET FILES

Parquet 파일에 대해서 DuckDB는 Parquet 메타데이터와 HTTP 범위 요청(range request)을 조합해, 질의가 실제로 요구하는 파일의 부분만 부분적으로 내려받는다. 이 한 문장이 이 장에서 가장 값진 문장이다.

CSV 파일에서 변환한 Parquet 파일이 GitHub에 놓여 있는 경우를 본다. 원서는 같은 파일을 놓고 질의문만 바꾸어 가며, 내려받는 양이 어떻게 줄어드는지를 네 걸음으로 보인다.

SQL① 전부 · ② 스키마만
# ① 파일 전체를 내려받아 DataFrame으로 바꾼다
conn.execute('''
  SELECT
    *
  FROM
  'https://github.com/weimenglee/DuckDB_Book/raw/
''').df()

# ② 내려받지 않고 원격 파일의 스키마만 얻는다
conn.execute('''
    DESCRIBE TABLE
'https://github.com/weimenglee/DuckDB_Book/raw/ma
''').df()
도판 8-5 · 8-6 · Parquet 파일 전체를 내려받은 DataFrame과, 내려받지 않고 얻은 스키마

순서가 중요하다. 대개는 파일의 모든 열이 필요하지 않다. 더 나은 접근은 먼저 내려받지 않고 원격 파일의 스키마를 얻고 그다음 어떤 열을 내려받을지 정하는 것이다. 답사자가 비석 앞에서 먼저 전체 도면을 그리고, 어느 면을 떠 올지 정하는 순서와 같다.

SQL③ 두 열 · ④ 한 열 · ⑤ 아무것도
# ③ Agency와 Agency Type 두 열만 내려받는다
conn.execute('''
  SELECT
    Agency, "Agency Type"
  FROM
    'https://github.com/weimenglee/DuckDB_Book/ra
''').df()

# ④ age 열만 내려받아 평균을 계산한다
conn.execute('''
   SELECT
     avg(age)
   FROM
    'https://github.com/weimenglee/DuckDB_Book/ra
''').df()

# ⑤ 데이터를 전혀 내려받지 않는다 — 메타데이터에서 읽는다
conn.execute('''
  SELECT
    count(*)
  FROM
    'https://github.com/weimenglee/DuckDB_Book/ra
''').df()
도판 8-7 · 8-8 · 8-9 · 두 열을 담은 DataFrame, age 열의 평균, 그리고 Parquet 파일의 행 수
질의실제로 통신선을 타고 오는 것내려받는 양
CSV · SELECT * 파일 전부. 행 기반이므로 쪼갤 수 없다
Parquet · SELECT * 파일 전부. 모든 열을 달라 했으므로
Parquet · DESCRIBE TABLE 스키마. 데이터는 오지 않는다
Parquet · 두 열 Agency와 "Agency Type"에 해당하는 부분
Parquet · avg(age) age 열에 해당하는 부분
Parquet · count(*) 없음. 메타데이터에 적힌 값을 읽는다

눈금의 길이는 정확한 비율이 아니다. 원서는 각 질의의 통신량을 수치로 밝히지 않으므로, 이 눈금은 “전부 · 두 열 · 한 열 · 없음”이라는 서술의 순서를 눈에 보이게 한 것이다. 열 개수만으로도 비율이 정해지지 않는다. Parquet은 열마다 압축이 걸리므로 열의 실제 크기가 서로 다르다.

다섯째 줄이 이 장의 정점이다. count(*)에는 데이터가 필요하지 않다. 행 수는 이미 파일 머리의 메타데이터에 적혀 있다. 비문의 글자 수를 알아내려고 굳이 종이와 먹을 챙겨 가지 않아도 되는 것과 같다. 그 수는 비석을 세울 때 이미 기록되었다.

이 절이 제1장·제2장·제4장의 이야기를 한자리에 모은다는 점도 짚어 둘 만하다. 열 기반 저장이라는 한 가지 결정이 제1장에서는 디스크 읽기를 줄였고, 제4장에서는 메모리 적재를 줄였고, 여기서는 네트워크 통신을 줄인다. 같은 구조가 세 층위에서 같은 방식으로 값을 한다.

Hugging Face라는 큰 서고

QUERYING HUGGING FACE DATASETS

httpfs 확장으로 HTTP(S) 프로토콜을 통해 원격 데이터셋에 질의하는 것 말고도, DuckDB는 Hugging Face가 호스팅하는 데이터셋에 대한 질의를 지원한다.

Hugging Face Datasets는 기계학습과 자연어 처리(NLP) 과제를 위한 대규모 데이터셋의 접근을 간단하게 만드는 라이브러리다. 모형의 훈련과 평가, 성능 견주기에 곧바로 쓸 수 있는 방대한 데이터셋 모음을 제공해, 손으로 데이터를 모으고 전처리할 필요를 없앤다. 아울러 데이터셋의 효율적인 적재·변환·분할을 위한 내장 함수로 사용자 정의 데이터 처리도 지원한다.

또한 Hugging Face Datasets는 분산되고 효율적인 데이터 적재에 최적화되어 있어 대규모 프로젝트에 이상적이다. Hugging Face Transformers 라이브러리와의 매끄러운 통합이 작업 흐름을 더욱 간결하게 만들며, NLP와 기계학습 과제에 종사하는 연구자와 개발자에게 값진 도구가 된다.

일러두기 — Hugging Face

Hugging Face는 NLP와 기계학습에 집중하는 회사이자 오픈소스 공동체다. 다양한 사전 훈련 기계학습 모형을 호스팅하며, 기계학습 프로젝트의 개발과 배포를 돕는 폭넓은 도구와 라이브러리와 자원을 제공한다.

DuckDB에서 Hugging Face 데이터셋을 쓰려면 먼저 그것이 어떻게 구성되어 있는지 이해해야 한다. 원서는 scikit-learn 라이브러리와 함께 배포되는 Tips 데이터셋을 예로 삼아, 웹 화면에서 URL을 조립하는 순서를 다섯 걸음으로 보인다.

  1. Hugging Face 데이터셋 페이지에서 쓰려는 데이터셋을 찾고 걸러 낸다.
  2. 그 데이터셋 페이지에서 사용자 이름데이터셋 이름을 얻는다.
  3. 같은 페이지에서 Files 탭을 눌러 이 데이터셋에서 쓸 수 있는 파일을 드러낸다.
  4. 내려받으려는 CSV·JSON·Parquet 파일을 찾는다. 이 예에서는 tips.csv다.
  5. 얻은 정보로 hf:// 경로를 조립한다.
검토자의 주석 — 도판 번호의 어긋남

원서는 “도판 8-14가 이 정보로 URL을 조립하는 방법을 보인다”고 적은 다음, 이어지는 절의 첫 문장에서 “도판 8-13에 보인 URL 형식을 써서”라고 적는다. 그런데 8-13은 내려받을 파일을 찾는 화면이고 URL을 조립하는 것은 8-14다. 뒤의 “8-13”은 8-14의 오기로 보인다.

Python + SQLhf:// 경로로 읽는다
import duckdb

conn = duckdb.connect()
conn.execute('''
  SELECT
    *
  FROM
    'hf://datasets/scikit-learn/tips/tips.csv';
''').df()
도판 8-15 · Hugging Face에서 내려온 tips 파일이 pandas DataFrame이 된다

원저자는 이어서 한 가지를 권한다. 데이터를 DuckDB 표에 저장해 두어, 뒤이은 질의마다 원격 종점에 접근하지 않도록 하라는 것이다.

SQL한 번만 가져와 표로 둔다
conn.execute('''
  CREATE TABLE Tips AS
  FROM
    'hf://datasets/scikit-learn/tips/tips.csv';
''')

# 이제 표에 질의한다
conn.execute('''
  SELECT
    *
  FROM
    Tips
''').df()
Tips 데이터셋이 Tips 표에 저장된다

이 권고에 이 장의 절충이 담겨 있다. 원격 질의는 편하지만 매번 통신한다. 앞 절에서 통신량을 아끼는 기법을 배웠는데, 여기서는 통신 횟수를 아끼는 기법이 나온다. 답사자가 같은 비석을 열 번 찾아가지 않고 탁본 한 장을 떠 와 책상에서 열 번 들여다보는 것과 같다.

폴더와 별표 · 경로를 늘려 잡는다

FOLDERS AND THE GLOB SYNTAX

Hugging Face 데이터셋의 파일이 특정 폴더에 저장된 경우가 있다. Adult Census Income 데이터셋이 그 예다. Files 탭을 누르면 data 폴더가 보이며, 그 안에 내려받으려는 파일이 있다. 파일 이름 옆에 표시된 아이콘을 누르고 파일 이름을 복사한다.

이 예에서 내려받으려는 것은 Parquet 파일이다. 온전한 URL은 다음과 같다. data 폴더 이름이 더해진 것을 눈여겨본다.

hf:// 경로폴더가 끼어든다
hf://datasets/AiresPucrs/adult-census-income/data/
train-00000-of-00001-7e70ed54d8cbb057.parquet
원서 본문의 이 URL은 줄이 바뀌는 자리에 사선이 보이지 않는다. 다만 이어지는 코드 조각이 '/data/train-…'으로 적고 있어, 사선이 지면에서 누락된 것으로 읽힌다
SQL경로를 두 토막으로 이어 붙인다
conn.execute('''
  SELECT
    *
  FROM
    'hf://datasets/AiresPucrs/adult-census-income'
    '/data/train-00000-of-00001-7e70ed54d8cbb057.parquet'
''').df()
도판 8-19 · Hugging Face에서 내려온 Parquet 파일이 DataFrame이 된다. 긴 경로를 두 개의 문자열로 나누어 나란히 두면 SQL이 이어 붙인다

여러 파일을 별표 하나로

제6장에서 다룬 glob 구문으로 여러 파일에 질의할 수 있다. 상기하는 뜻에서 glob 양상의 목록을 다시 옮긴다.

와일드카드하는 일
*임의 개수의 임의 문자와 일치한다. 없는 경우도 포함한다.
**임의 개수의 하위 디렉터리와 일치한다. 없는 경우도 포함한다.
?임의의 한 문자와 일치한다.
[abc]대괄호 안에 주어진 문자 가운데 하나와 일치한다.
[a-z]대괄호 안에 주어진 범위에서 한 문자와 일치한다.

어떤 데이터셋에 geo_test.csvgeo_train.csv 두 파일이 있다고 하자. 모든 CSV 파일을 적재하려면 별표를 쓴다.

SQLCSV 여러 개 · 그리고 여과
# 두 CSV 파일을 한꺼번에 적재한다
conn.execute('''
  SELECT
    *
  FROM
    'hf://datasets/gabrielwu/city_country/*.csv'
''').df()

# question 열에 'Huaibei'가 든 행을 찾는다
conn.execute('''
   SELECT
     *
   FROM
    'hf://datasets/gabrielwu/city_country/*.csv'
  WHERE question LIKE '%Huaibei%';
''').df()
도판 8-20 ~ 8-22 · 두 CSV 파일의 목록, 두 파일의 내용을 합친 DataFrame, 그리고 “Huaibei”가 든 한 행
주의 — 스키마가 같아야 한다

glob 양상으로 적재하는 모든 파일은 같은 스키마를 가져야 한다. 그렇지 않으면 예외가 발생한다.

그리고 glob 양상이 가장 값을 하는 자리는 Parquet 파일이다. 파일 집합 전체를 내려받을 필요가 흔히 없기 때문이다. 두 개의 Parquet 파일을 가진 데이터셋에서 question 열만 내려받고, 그 가운데 “happened”가 든 행만 표시한다.

SQL여러 Parquet에서 한 열만
conn.execute('''
  SELECT
    question
  FROM
    'hf://datasets/Stanford/web_questions/data/*
  WHERE question LIKE '%happened%';
''').df()
도판 8-23 · “happened”가 든 모든 질문을 돌려준다. 두 파일에 걸쳐, 그러나 한 열만 통신선을 탄다

여기서 앞 절의 눈금이 여러 파일로 확장된다. 별표 하나가 파일의 수를 늘리고, 열 이름 하나가 통신량을 줄인다. 두 방향이 서로 어긋나지 않는다는 점이 이 기법의 힘이다. 백 개의 비석을 훑으면서도 각 비석에서 한 줄만 떠 온다.

잠긴 서고 · 비공개 데이터셋

WORKING WITH PRIVATE HUGGING FACE DATASETS

지금까지는 공개 Hugging Face 데이터셋에 접근하는 법이었다. 비공개 데이터셋은 어떤가. 공개와 비공개의 주된 차이는, 비공개 데이터셋에는 접근하기 전에 해당 자격 증명을 제공해야 한다는 것이다. 익힐 것은 셋이다.

  • 만든다 CSV 파일을 Hugging Face에 올려 비공개 데이터셋을 만든다.
  • 열쇠를 얻는다 그 비공개 데이터셋에 접근할 액세스 토큰을 만든다.
  • 보인다 액세스 토큰을 제공해 DuckDB로 비공개 데이터셋에 원격 접근한다.

이 모든 일을 하기 전에 Hugging Face에 계정을 만들어야 한다. https://huggingface.co로 가서 Sign Up 버튼을 누르고, 전자우편 주소와 쓰려는 비밀번호를 넣고 Next를 누른다.

비공개 데이터셋을 올린다

  1. 사용자 아이콘을 누르고 New Dataset을 누른다.
  2. 새 데이터셋의 상세를 채운다. Owner 필드는 자신의 사용자 이름이어야 하며, 데이터셋이 Private으로 설정되었는지 확인한다. 그리고 Create dataset 버튼을 누른다.
  3. 다음 페이지에서 Files 탭을 누르고, Add file 버튼을 누르고, Upload files 항목을 누른다.
  4. 올리려는 파일을 끌어다 놓는다. 원서의 예에서는 Amazon.com의 과거 주가를 담은 CSV 파일이다.
  5. 같은 페이지 아래쪽에서 “Commit directly to the main branch”를 고른다. 올린 파일이 Files 탭에 나타난다.

액세스 토큰을 만든다

  1. 사용자 아이콘을 누르고 Settings를 누른다.
  2. Access Tokens를 누른다.
  3. New Token 버튼을 누른다.
  4. Read 버튼을 누르고, 이 토큰의 쓰임을 설명하는 이름을 넣는다.
  5. Create token 버튼을 누른다. 액세스 토큰이 생성된다.
  6. 복사 아이콘을 눌러 액세스 토큰을 복사하고 안전한 곳에 붙여 둔다.
주의 — 다시 보여 주지 않는다

이 토큰은 나중에 다시 와도 표시되지 않는다. 액세스 토큰을 잃으면 그것을 무효화하고 갱신해 새 토큰을 생성해야 한다.

인증하는 두 가지 길

액세스 토큰이 생성되었으므로 그것으로 DuckDB에서 비공개 데이터셋에 접근할 수 있다. DuckDB가 지원하는 주된 제공자(provider)는 둘이다.

제공자토큰이 놓이는 자리알맞은 상황
CONFIG CREATE SECRET 문에 액세스 토큰을 손으로 적는다. 곧 코드에 토큰이 노출된다. 특정하고 이미 아는 토큰으로 인증해야 할 때 이상적이다. 토큰이 자주 교체되는 환경, 또는 서비스마다 자격 증명을 명시적으로 지정해야 하는 환경이다.
CREDENTIAL_CHAIN 지역 컴퓨터의 디렉터리에서 토큰을 자동으로 찾아온다. 기본 위치, 환경 변수, 자격 증명 파일 등을 순서대로 확인하고, 유효한 인증 정보를 찾으면 멈춘다. 매끄러운 인증을 원할 때 유익하다. 클라우드 기반 가상 기계나, 자격 증명이 자동으로 저장되고 관리되는 지역 개발 환경이다.
Python + SQL① CONFIG — 코드에 적는다
import duckdb

conn = duckdb.connect()
conn.execute('''
   CREATE SECRET hf_token (
      TYPE HUGGINGFACE,
      TOKEN '<HuggingFace_Token>'
 );
''')

# 이제 공개 데이터셋의 파일에 접근하듯 쓴다
conn.execute('''
  SELECT
    *
  FROM
    'hf://datasets/Wei-Meng/StockPrices/AMZN.csv'
''').df()
도판 8-34 · 비공개 데이터셋의 Amazon 주가가 pandas DataFrame으로 표시된다

비공개 데이터셋에 접근하는 CONFIG 제공자 방법은 코드에 액세스 토큰을 노출한다. 액세스 토큰을 파일에 저장하는 CREDENTIAL_CHAIN 방법이 훨씬 안전하다. 먼저 Hugging Face Hub 파이썬 꾸러미를 설치하고 로그인 유틸리티를 실행한다.

Shell② CREDENTIAL_CHAIN — 파일에 둔다
$ pip install huggingface_hub

$ huggingface-cli login
Token: <HuggingFace_Token>

# 액세스 토큰을 붙여 넣는다. 화면에 되비침이 없다.
# git 자격 증명으로 추가할지 묻는다. n을 넣고 Enter를 누른다.

Add token as git credential? (Y/n) n
Token is valid (permission: read).
Your token has been saved to /Users/weimenglee/.cache/huggingface/token
Login successful
액세스 토큰이 ~/.cache/huggingface/ 디렉터리의 token이라는 파일에 저장된다
Python + SQL토큰 없이 같은 문장
import duckdb

conn = duckdb.connect()
conn.execute('''
  CREATE SECRET hf_token (
      TYPE HUGGINGFACE,
      PROVIDER CREDENTIAL_CHAIN
   );
''')
conn.execute('''
  SELECT
    *
  FROM
    'hf://datasets/Wei-Meng/StockPrices/AMZN.csv'
''').df()
출력은 도판 8-34와 같다. 질의문은 그대로이고 CREATE SECRET의 한 줄만 바뀐다
일러두기 — 토큰의 관리

토큰이 무효가 되면, 예컨대 Hugging Face에서 삭제했다면 ~/.cache/huggingface/ 디렉터리의 토큰 파일을 그냥 지우면 된다. 토큰이 바뀌면 그 토큰 파일에서 갱신할 수 있다.

제7장의 마지막 절이 여기서 되돌아온다. 그때도 세 가지 방법의 차이가 비밀번호를 어디에 두는가에 있었고, 운영체제나 파일에 맡기는 쪽이 권장되었다. 여기서도 같다. CREATE SECRET의 본문 한 줄이 TOKEN '…'에서 PROVIDER CREDENTIAL_CHAIN으로 바뀌는 것뿐인데, 그 한 줄이 코드에 열쇠를 박아 두는 일과 열쇠를 밖에 맡기는 일을 가른다. 열쇠를 문설주 밑에 두지 말라는 오래된 말과 다르지 않다.

맺음말 · SUMMARY

Parquet이 내용을 저장하는 방식 때문에, 원격 Parquet 파일을 가져오는 일은 특히 효율적이다. 필요한 열만 지역 기계로 내려받으면 되기 때문이다.

제8장에서는 DuckDB가 httpfs 확장으로 원격 파일(CSV, Parquet, JSON 등)을 손쉽게 가져오도록 돕는 모습을 보았다. Parquet이 내용을 저장하는 방식 때문에 원격 Parquet 파일을 가져오는 일은 특히 효율적이며, 필요한 열만 지역 기계로 내려받으면 된다.

아울러 DuckDB는 사전 훈련 기계학습 모형과 훈련 집합의 온라인 저장소인 Hugging Face의 데이터셋 접근을 지원한다. hf:// 경로로 공개와 비공개 양쪽의 Hugging Face 데이터셋 파일에 접근할 수 있다.

마지막으로 Hugging Face의 비공개 데이터셋에 접근하는 두 가지 방법, 곧 CONFIG 제공자 방법과 CREDENTIAL_CHAIN 제공자 방법을 익혔다.

답사를 마치며 이 장의 자리를 적어 둔다. 제8장은 새로운 문법이 가장 적은 장이다. INSTALLLOAD는 제2장에서, glob은 제6장에서, 자격 증명을 어디에 두는가는 제7장에서 이미 익혔다. 새로운 것은 httpfs라는 확장 이름과 hf://라는 경로 머리, 그리고 CREATE SECRET 한 문장이 전부다.

그런데 이 장은 총서에서 특별한 자리를 차지한다. 제1장의 주장이 세 번째로 값을 하는 장이기 때문이다. 데이터를 열 단위 기둥으로 세워 둔다는 그 한 가지 결정이, 제1장에서는 디스크에서 읽는 양을 줄였고, 제4장에서는 메모리에 올리는 양을 줄였고, 여기서는 통신선을 타고 오는 양을 줄인다. 층위가 바뀔 때마다 아끼는 것의 이름이 달라지지만 원리는 하나다. 잘 세운 구조는 어느 층위에서든 값을 한다.

그리고 count(*)가 한 바이트도 내려받지 않는 그 대목이 이 장의 정점이었다. 좋은 기록은 물건 자체를 뒤지지 않고도 답할 수 있는 물음을 미리 적어 둔다. 비석을 세운 이가 글자 수를 새겨 두었다면, 답사자는 종이를 펴지 않고도 그것을 옮겨 적을 수 있다.