28일차는 머신러닝 모델을 “어떻게 더 빠르게 돌릴 것인가”에서 시작해서, GPU 기반 머신러닝과 클라우드 환경 세팅까지 이어진 날이었다. 전날에는 Optuna로 Random Forest의 하이퍼파라미터를 탐색했다면, 이번에는 그 실험이 실제로 오래 걸릴 때 어떤 대안이 있는지 봤다. 대표적인 선택지는 GPU를 활용하는 방식, 더 좋은 머신을 빌리는 방식, 그리고 Spark 같은 분산 처리 환경을 쓰는 방식이었다.
특히 기억에 남은 건 GPU를 쓰면 무조건 빠르다가 아니라는 점이었다. 작은 데이터에서는 CPU 기반 scikit-learn이 더 빠를 수도 있고, GPU로 보내는 과정에서 오히려 병목이 생길 수 있다. 반대로 데이터가 충분히 크고 연산량이 많아지면 cudf, cuml, cupy 같은 GPU 기반 패키지나 클라우드 머신을 고려할 수 있었다. 오전에는 Optuna + cuML, XGBoost GPU 쪽을 봤고, 뒤쪽에서는 GCP에서 VM 인스턴스를 만들고 Jupyter Notebook 환경을 직접 여는 흐름까지 실습했다.
1. GPU 머신러닝은 기존 코드와 완전히 같지 않았다
처음에는 scikit-learn 코드를 GPU에서 그대로 빠르게 돌릴 수 있으면 좋겠다고 생각하기 쉽다. 실제로 NVIDIA에서는 scikit-learn 코드 변경 없이 cuML 가속을 붙이는 방식도 소개했지만, 실습에서는 완벽하게 호환되지는 않는다는 점을 봤다. 단일 모델을 한 번 fit()하고 predict()하는 정도는 잘 돌아갈 수 있지만, cross_val_score, K-Fold, 여러 번 반복되는 튜닝 구조에서는 충돌이나 호환 문제가 생길 수 있었다.
기존에 익숙한 구조는 아래와 같았다.
from sklearn.model_selection import cross_val_score
from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier(n_jobs=-1, random_state=1234)
scores = cross_val_score(
rf,
X_train,
y_train,
cv=kfold,
scoring="accuracy"
)
이런 코드는 CPU 기반 scikit-learn에서는 편하게 돌아간다. 하지만 GPU 기반으로 cuML을 쓰려면 내부 자료형, 모델 클래스, K-Fold 처리 방식이 달라진다. 특히 cross_val_score를 그대로 쓰기 어렵기 때문에, K-Fold를 직접 구현해야 하는 상황이 생겼다.
이 부분이 꽤 현실적이었다. 단순히 패키지 이름만 바꾸면 끝나는 것이 아니라, CPU 기반 라이브러리와 GPU 기반 라이브러리의 내부 처리 방식이 다르기 때문에 코드 구조도 조금 바뀌어야 했다.
2. GPU 사용 전에는
nvidia-smi
로 환경부터 확인했다
먼저 Colab에서 GPU가 제대로 잡혔는지 확인했다. 실습에서는 T4 GPU를 기준으로 진행했다. nvidia-smi를 실행하면 GPU 이름, 드라이버 버전, CUDA 버전, GPU 메모리 사용량 등을 확인할 수 있었다. 자료에서도 CUDA 버전은 나중에 PyTorch 같은 딥러닝 패키지와 호환성을 맞출 때 중요하다고 정리되어 있었다.
!nvidia-smi
출력에는 대략 이런 정보가 나왔다.
GPU Name: Tesla T4
Driver Version: 580.82.07
CUDA Version: 13.0
Memory: 15360MiB
GPU를 쓴다고 하면 모델 코드부터 생각하기 쉬운데, 실제로는 먼저 하드웨어와 CUDA 버전을 확인해야 했다. 특히 딥러닝을 하다 보면 특정 PyTorch 버전이 특정 CUDA 버전과 맞아야 하는 경우가 생긴다. 그래서 nvidia-smi는 단순 확인용이 아니라, 환경 문제를 추적할 때 기본으로 봐야 하는 명령어처럼 느껴졌다.
3. pandas와 scikit-learn 대신 cudf와 cuML을 사용했다
GPU 기반으로 전통적인 머신러닝을 하려면 기존의 pandas, numpy, scikit-learn을 그대로 쓰기보다, GPU를 지원하는 대체 패키지를 사용해야 했다. 자료에서는 전통적인 패키지와 GPU 기반 패키지를 아래처럼 대응해서 정리했다.
pandas → cudf
numpy → cupy
scikit-learn → cuml
실습 코드에서도 cudf, cupy, cuml을 가져왔다.
import cudf
import cupy
from cuml.model_selection import train_test_split
from cuml.metrics import accuracy_score
from cuml.ensemble import RandomForestClassifier as cuRandomForestClassifier
다만 모든 기능이 scikit-learn과 1:1로 대응되는 것은 아니었다. 예를 들어 train_test_split이나 accuracy_score처럼 비슷하게 제공되는 기능도 있지만, cross_val_score처럼 그대로 쓰기 어려운 기능도 있었다. 그래서 필요한 기능이 cuML 안에 있는지 먼저 확인하고, 없다면 직접 구현해야 했다.
4. cuML RandomForest는 파라미터 이름도 조금 달랐다
cuML의 RandomForestClassifier는 scikit-learn의 RandomForest와 비슷하지만, 세부 파라미터 이름이나 지원 값이 조금 달랐다. 예를 들어 scikit-learn에서는 분기 기준을 criterion으로 설정했지만, cuML에서는 split_criterion을 사용했다. 또 scikit-learn에서 사용할 수 있던 log_loss가 cuML 쪽에서는 동일하게 지원되지 않아 gini, entropy 정도로 조정해야 했다.
Optuna 목적 함수 안에서 cuML RandomForest용 파라미터를 다시 세팅했다.
def rf_objective(trial):
params = {
"n_estimators": trial.suggest_int("n_estimators", 10, 1000),
"split_criterion": trial.suggest_categorical(
"split_criterion",
["gini", "entropy"]
),
"max_depth": trial.suggest_int("max_depth", 2, 50),
"max_features": trial.suggest_int(
"max_features",
2,
X_train.shape[1]
),
"min_samples_split": trial.suggest_int(
"min_samples_split",
3,
10
)
}
scores = []
for train_index, val_index in kfold.split(X_train):
X_train_part = X_train.iloc[train_index, :]
X_val_part = X_train.iloc[val_index, :]
y_train_part = y_train.iloc[train_index]
y_val_part = y_train.iloc[val_index]
X_train_cu = cudf.DataFrame(X_train_part)
X_val_cu = cudf.DataFrame(X_val_part)
y_train_cu = cudf.Series(y_train_part)
y_val_cu = cudf.Series(y_val_part)
rf = cuRandomForestClassifier(
**params,
random_state=1234,
max_batch_size=4096,
n_bins=16,
n_streams=4
)
rf.fit(X_train_cu, y_train_cu)
val_acc = accuracy_score(
y_val_cu,
rf.predict(X_val_cu)
)
scores.append(val_acc)
acc_mean = sum(scores) / len(scores)
return acc_mean
여기서 이전 코드와 가장 크게 달라진 부분은 두 가지였다. 첫째, K-Fold를 직접 돌렸다. 둘째, 각 fold마다 pandas DataFrame을 cudf.DataFrame으로 바꿔서 GPU에서 처리할 수 있게 했다.
이 부분은 귀찮긴 했지만, 내부에서 무슨 일이 일어나는지 이해하기에는 좋았다. cross_val_score가 자동으로 해주던 train/validation 분할, 학습, 평가, 점수 저장 과정을 직접 구현한 셈이다.
5. K-Fold를 직접 구현하면서 자료형 변환이 중요해졌다
기존 scikit-learn에서는 cross_val_score()에 모델과 데이터를 넣으면 알아서 여러 fold를 돌려줬다. 그런데 cuML에서는 이 흐름을 직접 만들어야 했다. kfold.split(X_train)이 각 fold의 train index와 validation index를 주면, 그 인덱스로 데이터를 직접 나눠야 했다.
for train_index, val_index in kfold.split(X_train):
X_train_part = X_train.iloc[train_index, :]
X_val_part = X_train.iloc[val_index, :]
y_train_part = y_train.iloc[train_index]
y_val_part = y_train.iloc[val_index]
그 다음이 중요했다. 원래 X_train_part, X_val_part는 pandas DataFrame이다. 그런데 cuML RandomForest에 넣으려면 cudf.DataFrame, cudf.Series로 바꿔주는 것이 더 자연스럽다.
X_train_cu = cudf.DataFrame(X_train_part)
X_val_cu = cudf.DataFrame(X_val_part)
y_train_cu = cudf.Series(y_train_part)
y_val_cu = cudf.Series(y_val_part)
GPU를 쓴다고 해도 중간중간 CPU 자료형과 GPU 자료형이 계속 왔다 갔다 하면 병목이 생길 수 있다. 자료에서도 GPU 기반으로 하려면 자료형을 맞추는 것이 중요하고, 맞추지 않으면 병목현상이 생길 수 있다고 설명했다.
6. GPU 병렬 처리 옵션도 따로 봐야 했다
cuML RandomForest에는 scikit-learn에서 보지 못했던 GPU 관련 옵션도 있었다. 실습에서는 max_batch_size, n_bins, n_streams를 설정했다.
rf = cuRandomForestClassifier(
**params,
random_state=1234,
max_batch_size=4096,
n_bins=16,
n_streams=4
)
각 옵션의 느낌은 이렇게 정리됐다.
max_batch_size
= 한 번에 GPU 메모리에 올릴 데이터 크기와 관련
n_bins
= 분기 기준을 만들 때 사용하는 bin 개수와 관련
n_streams
= 내부적으로 몇 개 흐름으로 나눠 병렬 처리할지와 관련
이 값들은 단순히 모델 성능만 바꾸는 파라미터라기보다, GPU 메모리와 병렬 처리 방식에 영향을 주는 설정에 가까웠다. 데이터가 아주 크면 max_batch_size를 무작정 크게 잡을 수 없고, n_streams도 하드웨어와 데이터 크기에 따라 조절이 필요했다. 강의에서도 큰 데이터셋에서는 GPU 메모리와 처리 단위를 보면서 조절해야 한다고 설명했다.
이 지점이 CPU 기반 scikit-learn과 달랐다. scikit-learn에서는 주로 모델 성능과 관련된 파라미터를 봤다면, GPU 환경에서는 하드웨어가 감당할 수 있는 처리 단위도 같이 봐야 했다.
7. Optuna 실행 시
n_jobs=1
로 조정했다
Optuna를 실행할 때도 n_jobs=-1을 무조건 쓰면 안 된다는 점을 봤다. CPU 기반 실험에서는 모든 코어를 쓰기 위해 n_jobs=-1을 자주 사용했지만, GPU 병렬 처리에서는 오히려 병목이나 충돌이 생길 수 있었다. 자료에서도 GPU로 병렬 처리를 할 때는 상황에 따라 다르지만 n_jobs=1로 두는 경우가 많다고 정리되어 있었다.
import optuna
rf_study = optuna.create_study(direction="maximize")
rf_study.optimize(
rf_objective,
n_trials=100,
n_jobs=1
)
이 부분은 앞으로 계속 조심해야 할 것 같다. CPU에서 통하던 습관이 GPU에서도 그대로 통하지는 않는다. 특히 GPU 하나를 여러 작업이 동시에 쓰려고 하면 오히려 느려지거나 에러가 날 수 있다.
8. 작은 데이터에서는 GPU가 오히려 느릴 수 있었다
가장 크게 남은 문장 중 하나는 “GPU를 쓰면 무조건 빠르다”가 아니라는 점이었다. 타이타닉 데이터처럼 샘플 수와 컬럼 수가 적은 데이터에서는 GPU로 옮기고, cuDF로 변환하고, 다시 평가하는 과정의 비용이 더 클 수 있다. 실제로 이런 작은 데이터는 그냥 scikit-learn으로 CPU에서 돌리는 것이 더 나을 수 있었다.
작은 데이터
→ GPU 전송/변환 비용이 더 커질 수 있음
→ scikit-learn CPU가 더 나을 수 있음
큰 데이터
→ 반복 학습과 연산량이 커짐
→ GPU 기반 cuML, XGBoost GPU를 고려할 수 있음
이 부분은 도구 선택에서 중요했다. GPU는 무조건 상위호환이 아니라, 데이터 크기와 연산 구조가 맞을 때 효과가 있다. 샘플 데이터에서는 GPU를 쓰는 법을 연습하는 의미가 더 컸고, 실제 효과는 데이터가 더 커졌을 때 기대할 수 있다고 보는 게 맞았다.
9. XGBoost는 cuML이 아니라 자체 GPU 옵션을 사용했다
RandomForest는 cuML 쪽 모델을 사용했지만, XGBoost는 cuML에 따로 들어 있는 것이 아니라 자체 패키지에서 GPU를 지원했다. 그래서 XGBClassifier를 그대로 사용하되, 파라미터에 device="cuda"를 넣었다. 자료에서도 XGBoost나 LightGBM 계열은 cuML에서 별도 모델을 제공하기보다, 자체 패키지에서 GPU 지원 옵션을 제공한다고 설명했다.
from xgboost import XGBClassifier
def xgb_objective(trial):
params = {
"n_estimators": trial.suggest_int("n_estimators", 10, 1000),
"max_depth": trial.suggest_int("max_depth", 2, 50),
"learning_rate": trial.suggest_float(
"learning_rate",
0.01,
0.3
)
}
scores = []
for train_index, val_index in kfold.split(X_train):
X_train_part = X_train.iloc[train_index, :]
X_val_part = X_train.iloc[val_index, :]
y_train_part = y_train.iloc[train_index]
y_val_part = y_train.iloc[val_index]
X_train_cu = cudf.DataFrame(X_train_part)
X_val_cu = cudf.DataFrame(X_val_part)
y_train_cu = cudf.Series(y_train_part)
y_val_cu = cudf.Series(y_val_part)
xgb = XGBClassifier(
**params,
random_state=1234,
device="cuda"
)
xgb.fit(X_train_cu, y_train_cu)
val_acc = accuracy_score(
y_val_cu,
xgb.predict(X_val_cu)
)
scores.append(val_acc)
acc_mean = sum(scores) / len(scores)
return acc_mean
이 코드는 RandomForest 때와 거의 비슷한 구조를 유지했다. 달라진 것은 모델이 cuRandomForestClassifier에서 XGBClassifier로 바뀌고, GPU 사용 옵션으로 device="cuda"를 넣었다는 점이었다.
xgb_study = optuna.create_study(direction="maximize")
xgb_study.optimize(
xgb_objective,
n_trials=100,
n_jobs=1
)
출력 로그에서는 trial별 accuracy가 찍혔고, 어떤 trial에서는 0.83 근처 값도 나왔다. 다만 여기서도 중요한 건 점수 자체보다, pandas - scikit-learn - optuna - cudf - cuml - cuda - xgboost가 섞이면 어디서든 버전이나 자료형 충돌이 생길 수 있다는 점이었다.
10. GCP에서는 먼저 방화벽부터 열어야 했다
뒤쪽에서는 Colab이 아니라 GCP에서 직접 좋은 머신을 빌려 쓰는 흐름으로 넘어갔다. 목적은 구글 데이터센터에 있는 VM 인스턴스를 빌리고, 거기에 Anaconda와 Jupyter Notebook을 설치해서 내 브라우저에서 접속하는 것이었다.
처음에 바로 VM부터 만들면 될 것 같지만, 실제로는 방화벽 설정이 먼저 필요했다. Jupyter Notebook은 기본적으로 8888번 포트를 사용하므로, 외부에서 이 포트로 들어올 수 있도록 방화벽 규칙을 만들어야 했다. GCP 자료에서도 VPC 네트워크 → 방화벽 → 방화벽 규칙 만들기로 들어가고, TCP 8888 포트를 열어야 한다는 흐름이 화면 캡처와 함께 정리되어 있었다.
방화벽 규칙에서 중요한 설정은 대략 이랬다.
대상 태그
= 나중에 VM 인스턴스에 붙일 태그와 동일하게 설정
소스 IPv4 범위
= 0.0.0.0/0
프로토콜 및 포트
= TCP 8888
회사 환경이라면 특정 IP만 열어두는 것이 맞지만, 실습에서는 집, 카페, 강의장 등 어디서든 접속할 수 있게 0.0.0.0/0으로 열었다. 다만 이 설정은 보안상 넓게 열어두는 것이므로, 실제 운영 환경에서는 훨씬 조심해야 한다.
11. VM 인스턴스는 하드웨어, OS, 네트워크를 같이 주문하는 느낌이었다
방화벽을 만든 뒤에는 Compute Engine에서 VM 인스턴스를 만들었다. 여기서 인스턴스 이름, 리전, 머신 유형, OS, 디스크 크기, 네트워크 태그를 설정했다. 자료에서는 E2 계열 머신을 선택하고, Ubuntu 계열 OS와 100GB 정도의 디스크를 설정하는 화면이 정리되어 있었다.
머신을 선택할 때는 단순히 CPU 개수만 보는 것이 아니라, 메모리와 지역도 같이 봐야 했다. 서울 리전은 지연시간이 짧지만 비용이나 할당 가능한 자원이 다를 수 있고, GPU는 지역에 따라 지원 여부가 달랐다. 실제로 GCP에서는 특정 리전에 원하는 GPU가 없거나 이미 할당이 꽉 차 있을 수 있었다.
네트워크 설정에서는 HTTP/HTTPS 트래픽을 허용하고, 아까 만든 방화벽 태그를 VM에도 붙였다. 이걸 빼먹으면 VM은 만들어졌는데 Jupyter Notebook으로 접속이 안 되는 문제가 생길 수 있다.
방화벽 규칙 생성
→ VM 인스턴스 생성
→ VM 네트워크 태그에 방화벽 태그 연결
→ 외부 IP:8888로 접속
VM을 만들고 초록색 체크가 뜨면 머신이 켜진 상태다. 이때부터는 비용이 발생할 수 있으므로, 실습이 끝나면 중지하거나 삭제해야 한다는 점도 중요했다.
12. SSH로 접속해서 Anaconda를 설치했다
VM 인스턴스가 만들어지면 SSH로 접속했다. 접속하면 Ubuntu 터미널이 열리고, 여기서부터는 리눅스 환경 세팅을 직접 해야 했다. 먼저 root 계정 비밀번호를 설정하고 root로 전환했다.
sudo passwd
su -
그 다음 Anaconda 설치 파일을 서버 안에서 다운로드했다. 로컬 PC에 다운로드하는 것이 아니라, VM 서버에 직접 다운로드해야 하므로 wget을 사용했다. 자료에서는 Anaconda archive에서 2024년 2월 버전의 Linux x86_64 설치 파일을 가져오는 예시가 있었다.
wget https://repo.anaconda.com/archive/Anaconda3-2024.02-1-Linux-x86_64.sh
다운로드 후에는 bash로 설치 파일을 실행했다.
bash Anaconda3-2024.02-1-Linux-x86_64.sh
설치 중에는 라이선스 화면에서 엔터를 눌러 넘기고, yes를 입력해야 했다. 설치 경로는 기본값인 /root/anaconda3를 사용했다. 설치가 끝난 뒤에는 .bashrc를 반영해서 일반 리눅스 쉘에서 Anaconda 명령어를 쓸 수 있게 했다.
source ~/.bashrc
이 부분은 예전에 로컬에서 Anaconda를 설치했던 것과 비슷하지만, GUI 설치창이 아니라 터미널로 직접 진행한다는 점이 달랐다.
13. Jupyter Notebook 환경 설정 파일을 직접 수정했다
Anaconda 설치 후에는 Jupyter Notebook을 외부에서 접속할 수 있게 설정해야 했다. 먼저 설정 파일을 생성했다.
jupyter notebook --generate-config
그 다음 설정 파일을 열어 수정했다.
vi /root/.jupyter/jupyter_notebook_config.py
실습에서는 Ubuntu minimal 버전을 사용했기 때문에 vim이 없을 수 있었다. 이 경우 apt-get으로 설치했다.
sudo apt-get update
sudo apt-get install vim
설정 파일에는 Jupyter Notebook이 외부 접속을 받을 수 있도록 IP, 포트, 브라우저 옵션을 넣었다. GCP 자료에서도 이 세 줄을 추가하는 방식이 정리되어 있었다.
c.NotebookApp.ip = "*"
c.NotebookApp.port = 8888
c.NotebookApp.open_browser = False
ip="*"는 외부 접속을 허용하는 설정이고, port=8888은 방화벽에서 열었던 포트와 맞추는 설정이다. open_browser=False는 서버에서 브라우저를 직접 열지 않도록 하는 설정이다. 로컬 PC에서는 Jupyter를 실행하면 자동으로 브라우저가 열리지만, GCP VM은 서버 환경이므로 브라우저를 띄울 수 없다.
수정 후에는 저장하고 나와야 한다. vi에서는 a로 insert 모드에 들어가고, 수정이 끝나면 ESC를 누른 뒤 :wq로 저장하고 종료했다.
14. 외부 IP와 토큰으로 Jupyter Notebook에 접속했다
설정이 끝나면 Jupyter Notebook을 실행했다.
jupyter-notebook --no-browser --port=8888 --allow-root
이제 VM 안에서 Jupyter Notebook 서버가 켜진다. 내 브라우저에서는 VM의 외부 IP와 포트 번호를 조합해서 접속한다.
http://외부IP주소:8888
접속하면 토큰을 입력하라는 화면이 나온다. 이 토큰은 터미널에 출력된 Jupyter Notebook URL 뒤쪽에 붙어 있다. 이 부분에서 root 비밀번호인 1234 같은 값을 넣는 것이 아니라, Jupyter Notebook이 출력한 token 값을 복사해서 넣어야 했다.
이 흐름을 거치면 로컬 브라우저에서 GCP VM 위에 떠 있는 Jupyter Notebook에 접속할 수 있다. 즉 내 노트북에서 계산하는 것이 아니라, 구글 클라우드에 빌린 머신에서 Python 코드를 실행하는 구조다.
15. 클라우드 머신은 꼭 끄거나 삭제해야 했다
마지막으로 중요한 것은 종료였다. SSH 창을 닫거나 Jupyter Notebook을 끄는 것만으로는 VM 인스턴스가 삭제되지 않는다. VM이 계속 켜져 있으면 비용이 계속 발생할 수 있다. 실습 자료에서도 초록색 상태는 머신이 켜져 있다는 뜻이고, 필요 없으면 중지하거나 삭제해야 한다고 강조했다.
Jupyter Notebook 종료
= 서버에서 실행 중인 프로세스 종료
VM 중지
= 머신 전원 끄기
VM 삭제
= 인스턴스와 연결 리소스 제거
실습용으로 잠깐 만든 머신이라면 삭제까지 하는 것이 안전하다. 다만 디스크나 고정 IP 같은 리소스가 남아 있으면 비용이 계속 발생할 수 있으므로, 나중에는 어떤 리소스가 남았는지 확인하는 습관이 필요할 것 같다.
마무리
28일차는 머신러닝 실험을 빠르게 돌리기 위한 현실적인 선택지를 본 날이었다. CPU 기반 scikit-learn으로 충분한 경우도 있지만, 데이터가 커지고 하이퍼파라미터 튜닝 횟수가 많아지면 GPU 기반 cuML이나 XGBoost GPU, 또는 클라우드 머신을 고려해야 했다. 다만 GPU는 무조건 빠른 것이 아니고, 데이터가 작으면 변환 비용과 병목 때문에 오히려 느릴 수 있다는 점도 같이 봤다.
코드 쪽에서는 cuML RandomForest를 쓰기 위해 pandas DataFrame을 cudf.DataFrame으로 바꾸고, cross_val_score 대신 K-Fold를 직접 구현했다. XGBoost는 cuML 모델이 아니라 자체 패키지의 device="cuda" 옵션을 사용했다. 이 과정에서 GPU 기반 코드에서는 모델 파라미터뿐 아니라 CUDA 버전, 자료형, 병렬 처리 옵션, 패키지 호환성까지 같이 봐야 한다는 점이 남았다.
GCP 실습에서는 VM 인스턴스를 만드는 것보다 방화벽, 포트, 네트워크 태그, Anaconda 설치, Jupyter 설정까지 연결하는 과정이 더 중요했다. 특히 8888 포트를 열고, 외부 IP와 Jupyter token으로 접속하는 과정이 로컬 환경과 달랐다. 앞으로 데이터나 모델이 커졌을 때는 단순히 “더 좋은 컴퓨터를 쓰자”가 아니라, 그 컴퓨터에 접속하고 환경을 구성하고 비용을 관리하는 것까지 함께 생각해야 할 것 같다.
28일차는 GPU 기반 머신러닝과 GCP VM 환경을 통해, 모델 성능 이전에 실행 환경과 연산 구조를 어떻게 설계해야 하는지 본 날이었다.
'[SK플래닛] ASAC 빅데이터전문가 11기 > 학습기록' 카테고리의 다른 글
| [SK플래닛] ASAC 빅데이터전문가 11기 | 31일차 (3) | 2026.06.01 |
|---|---|
| [SK플래닛] ASAC 빅데이터전문가 11기 | 29일차 (0) | 2026.05.29 |
| [SK플래닛] ASAC 빅데이터전문가 11기 | 27일차 (0) | 2026.05.27 |
| [SK플래닛] ASAC 빅데이터전문가 11기 | 26일차 (0) | 2026.05.26 |
| [SK플래닛] ASAC 빅데이터전문가 11기 | 25일차 (1) | 2026.05.22 |
