segmentation이 "어디에 뭐가 있냐" 를 찾는 거라면,
캡셔닝은 "이 장면을 문장으로 설명" 하는 것
핵심 차이는 "개별 객체를 구분하느냐" 
Semantic Segmentation은 픽셀마다 "클래스"를 붙이는 것. 사람이 두 명 있어도 둘 다 person 레이블이 붙기 때문에 "어디에 사람이 있나"는 알지만 "몇 명인지, 어디서 어디까지가 한 명인지"는 모름.
Instance Segmentation은 거기서 한 발 더 나아가서, 같은 클래스 안에서도 개별 객체를 구분. person_1, person_2처럼 각각 다른 마스크를 가짐. 사람이 겹쳐 있어도 분리 가능.
| Semantic | Instance | |
|---|---|---|
| 자율주행 | 도로/보도 구분 | 보행자 각각 추적 |
| 의료 영상 | 장기 영역 분리 | 세포 개수 세기 |
| 사진 편집 | 배경 교체 | 특정 사람만 블러 |
클라우드 스토리지(Azure Blob Storage, S3 등)에 새로 업로드된 파일을 자동으로 감지해서 읽어오는 Databricks 기능이다.
클라우드 스토리지에 새 파일 업로드
↓
Auto Loader가 자동 감지
↓
Spark로 증분 처리 (새 파일만)
일반 spark.read는 실행할 때 존재하는 파일만 읽고 끝나지만, Auto Loader는 새 파일이 생길 때마다 자동으로 처리한다.
Auto Loader를 사용하기 위한 Databricks 전용 Spark 포맷 이름이다.
# 일반 포맷 (파일 형식)
spark.read.format("csv")
spark.read.format("parquet")
# Auto Loader 전용 포맷
spark.readStream.format("cloudFiles") # ← 이게 Auto Loader 켜는 스위치
format() 안에 넣는다는 점은 같지만, cloudFiles는 파일 형식이 아니라 Auto Loader 기능을 활성화하는 키워드다. Databricks 외부 환경에서는 사용 불가.
df = (spark.readStream
.format("cloudFiles")
.option("cloudFiles.format", "json") # 실제 파일 형식은 여기서 지정
.option("cloudFiles.schemaLocation", "/checkpoint/schema")
.load("/mnt/storage/incoming/")
)
cloudFiles.format만 바꿔서 CSV, JSON, Parquet, 이미지 등 다양한 형식 처리스트리밍 처리에서 "어디까지 처리했는지"를 기록해두는 위치다.
파일 100개 처리 중 → 클러스터 갑자기 재시작
↓
Checkpoint 없으면: 처음부터 다시 (중복 처리)
Checkpoint 있으면: 87번까지 했으니 88번부터 이어서
query = (df.writeStream
.format("delta")
.option("checkpointLocation", "/checkpoint/my_pipeline") # ← 여기 지정
.start("/mnt/output/")
)
/tmp에 저장하면 클러스터 재시작 시 날아가므로 주의# 좋은 예
/checkpoint/user_logs_pipeline
/checkpoint/image_caption_pipeline
# 나쁜 예
/checkpoint/temp ← 여러 파이프라인이 공유하면 충돌
데이터 구조가 바뀌어도 (새 컬럼 추가 등) 자동으로 스키마를 업데이트해주는 기능이다.
1월 데이터: { "name": "철수", "age": 25 }
2월 데이터: { "name": "영희", "age": 23, "email": "y@test.com" } ← 새 컬럼 추가
Schema Evolution 없으면 2월 데이터 읽을 때 에러 발생. 있으면 자동으로 email 컬럼 추가해서 처리.
df = (spark.readStream
.format("cloudFiles")
.option("cloudFiles.format", "json")
.option("cloudFiles.schemaLocation", "/checkpoint/schema") # 스키마 저장 위치
.option("cloudFiles.inferColumnTypes", "true") # 컬럼 타입 자동 추론
.load("/mnt/storage/incoming/")
)
주의: Schema Evolution은 컬럼 추가는 자동 처리하지만, 컬럼 삭제나 타입 변경은 수동으로 처리해야 할 수 있다.
실제 프로젝트에서 네 가지를 합쳐서 쓰면 이렇게 된다:
# 이미지 캡셔닝 파이프라인 예시
df = (spark.readStream
.format("cloudFiles") # Auto Loader ON
.option("cloudFiles.format", "binaryFile") # 이미지 파일
.option("cloudFiles.schemaLocation", "/checkpoint/schema") # Schema Evolution
.load("/mnt/storage/images/") # 새 이미지 감지
)
query = (df.writeStream
.format("delta")
.option("checkpointLocation", "/checkpoint/image_pipeline") # Checkpoint
.start("/mnt/output/captions/")
)
새 이미지 업로드
↓
cloudFiles 감지 (Auto Loader)
↓
Schema Evolution으로 구조 변화 자동 대응
↓
처리 결과 저장 + Checkpoint 기록
↓
클러스터 재시작해도 이어서 처리 가능
Auto Loader로 읽어온 데이터를 Delta Lake에 저장하는데, Delta Lake가 빠른 이유가 바로 Column Store 방식이기 때문이다.
데이터를 행(Row) 단위로 저장하느냐, 열(Column) 단위로 저장하느냐에 따라 성능이 크게 달라진다.
예시 테이블:
| id | name | age | salary |
|---|---|---|---|
| 1 | 철수 | 25 | 3000 |
| 2 | 영희 | 23 | 4000 |
| 3 | 민수 | 30 | 5000 |
Row Store (MySQL 등 일반 DB)
디스크: [1, 철수, 25, 3000] [2, 영희, 23, 4000] [3, 민수, 30, 5000]
Column Store (Parquet, Delta Lake)
디스크: [1, 2, 3] [철수, 영희, 민수] [25, 23, 30] [3000, 4000, 5000]
SELECT AVG(salary) FROM employees
필요한 컬럼만 골라 읽으니 I/O가 압도적으로 줄어든다.
같은 컬럼끼리 모여 있으면 비슷한 값들이 연속으로 나열되어 압축이 잘 된다.
age 컬럼: [25, 23, 30, 28, 25, 24...] ← 비슷한 숫자 → 압축 잘 됨
salary 컬럼: [3000, 3000, 4000, 3000...] ← 중복 많음 → 압축 잘 됨
Parquet, Delta Lake 모두 Column Store 방식이다. 대용량 데이터를 다뤄도 빠른 이유가 여기에 있다.
| Row Store | Column Store | |
|---|---|---|
| 대표 DB | MySQL, PostgreSQL | Parquet, Delta Lake |
| 적합한 용도 | 단건 조회, 트랜잭션 | 대용량 분석, 집계 |
| 특정 컬럼 조회 | 느림 | 빠름 |
| 압축률 | 낮음 | 높음 |