BigQuery 원격 함수는 Cloud Functions 및 Cloud Run과 BigQuery의 직접적인 통합을 제공하여 BigQuery 외부 소프트웨어에서 GoogleSQL 기능을 사용할 수 있는 것이다.
원하는 언어로 Cloud Functions 또는 Cloud Run을 구성하여 함수를 배포한 후 BigQuery에서 쿼리문을 통해 호출할 수 있다.
아래와 같은 강아지 이미지에 대한 객체 테이블을 생성하고 원격 함수에서 Vision API를 사용해 Label Detection을 진행해볼 것이다.
이미지 예시
보더콜리
퍼그
데이터셋은 Stanford Dogs Dataset 여기에서 다운받았다.
무수히 많은 강아지들에 대한 이미지 데이터들이 있다.
보더콜리와 퍼그에 대한 데이터만 따로 사용해볼 것이다.
사전 작업
# 샘플 추출
mkdir sample
mv n02106166-Border_collie/ sample/
mv n02110958-pug/ sample/
cd sample/
gsutil cp -r n02106166-Border_collie/ gs://<버킷명>/<강아지 폴더>/<보더콜리 폴더>
gsutil cp -r n02110958-pug/ gs://<버킷명>/<강아지 폴더>/<퍼그 폴더>
BigQuery 객체 테이블은 외부 테이블로 Cloud Storage의 데이터와 커넥션을 맺어서 생성할 수 있다. 때문에 먼저 Cloud Storage와 맺을 커넥션을 생성해줘야 한다.
Cloud Storage의 버킷 리전과 동일해야 한다.
CREATE OR REPLACE EXTERNAL TABLE
`<데이터셋>.<테이블>`
WITH CONNECTION
`<리전>.<커넥션 이름>`
OPTIONS(
object_metadata="SIMPLE",
uris=["gs://<버킷명>/<강아지 폴더>/*"]
);
이런 메타데이터들이 포함된 객체 테이블이 생성됐다.
HTTP를 통해 트리거링을 해줌으로써 BigQuery에서 이 HTTP url을 통해 Functions을 호출할 것이다.
런타임 : Python 3.11
진입점 : label_detection
main.py
import urllib.request
import flask
import functions_framework
from google.cloud import vision
@functions_framework.http
def label_detection(request: flask.Request) -> flask.Response:
try:
client = vision.ImageAnnotatorClient()
calls = request.get_json()['calls']
replies = []
for call in calls:
content = urllib.request.urlopen(call[0]).read()
results = client.label_detection({'content': content})
replies.append(vision.AnnotateImageResponse.to_dict(results))
return flask.make_response(flask.jsonify({'replies': replies}))
except Exception as e:
return flask.make_response(flask.jsonify({'errorMessage': str(e)}), 400)
requirements.txt
Flask==2.2.2
functions-framework==3.3.0
google-cloud-vision==3.2.0
원격함수 생성
CREATE OR REPLACE FUNCTION
`<데이터셋>.<테이블>` (signed_url_ STRING)
RETURNS
JSON REMOTE
WITH CONNECTION
`<리전>.<커넥션 이름>`
OPTIONS(
endpoint = '<트리거 URL>',
max_batching_rows = 1
);
BigQuery에 루틴이 생성되고 아래와 같은 함수가 생성된다.
(루틴은 BigQuery에서 저장 프로시져, 원격 함수를 포함한 UDF, 테이블 함수를 포함하는 리소스 유형)
이제 이 함수를 쿼리문으로 호출함으로써 Label Detection을 해볼 것이다.
원격 함수 호출
SELECT
uri, <데이터셋>.<원격 함수>(signed_url)
FROM
EXTERNAL_OBJECT_TRANSFORM(
TABLE `<데이터셋>.<객체 테이블>`,
["SIGNED_URL"]
)
WHERE REGEXP_CONTAINS(
uri,
r'<정규식>'
)
LIMIT 5;
이 간단한 쿼리문으로 Vision API를 통해 이미지에 대한 Label Detection이 수행된다. 여기선 보더콜리 이미지로 테스트 해봤다.
'Dog' 라벨을 발견할 수 있었고 score는 accuracy라고 생각하면 되는데 중간중간 이미지에 따라서 불분명하게 나온 것도 있지만 상당히 높은 편이다.
위의 과정을 수행하면서 발생할 수 있는 권한 에러가 두가지 있다.
해결 법은 아래와 같다.
1. Cloud Storage에 대한 권한 부족
Access Denied : BigQuery BigQuery:Permission denied while gobbling file pattern. <서비스 계정> does not have storage.object.list access to the Google Cloud Storage bucket. Permission 'storage.object.list' denied on resource (or it may not exist). Please make sure 'gs://<버킷명>' is accessible via appropriate IAM roles, e.g. Storage Object Viewer or Storage Object Creator.
Cloud Storage에 위의 에러에서 표시된 서비스 계정에 대한 storage.object.list 권한을 부여해주면 된다.
2. 외부 테이블에 대한 커넥션이 Cloud Functions에 권한이 없는 경우
Access Denied : BigQuery Biguery: Received response code 403 from endpoint https://<리전>.<프로젝트 ID>.cloudfunctions.net/label_detection; Make sure the service account associated with the connection <프로젝트 ID>.<리전>.<커넥션 이름>is granted the cloudfunctions.functions.invoke permission (e.g., via the Cloud Functions Invoker role) on your Cloud Function endpoint,or the run.routes.invoke permission (e.g., via the Cloud Run Invoker role) on your Cloud Run endpoint. Please allow 60 seconds for the permission change to propagate before retrying.
IAM에서 커넥션이 사용하는 서비스 계정에 대해 Cloud Functions Invoker 권한을 부여해주면 된다.
[BigQuery 원격 함수를 사용한 Object Table Label Detection 참고]
https://qiita.com/kosuke21/items/18fe7fea9fd7d265578c