인지야공/강의 요약/2번째 글
IoT 플랫폼과 IoT 장치 — 라즈베리파이에서 MQTT까지
이 과목은 “센서에서 화면까지 한 번 관통해 보는 것”이 목표였다. 라즈베리파이에서 카메라로 찍고, PyTorch 로 분류하고, MQTT 로 보내고, Flask 로 띄운다. 각 조각은 단순한데 이어 붙이면 시스템이 된다.
개발 환경
| 종류 | 하는 일 | 도구 |
|---|---|---|
| 파이썬 인터프리터 | 코드 해석·실행 | Python, Anaconda(배포판) |
| 통합 개발 환경 | 편집·실행·수정·배포 | Jupyter, PyCharm |
| 통계·전처리 | 로드와 연산 | NumPy, Pandas, SciPy |
| 시각화 | Matplotlib, Seaborn | |
| 머신러닝 | 학습·추론 | scikit-learn |
| GPU 가속화 | 가속 연산 지원 | CUDA, cuDNN |
| 딥러닝 프레임워크 | 학습·추론 | TensorFlow, PyTorch |
가상환경을 프로젝트마다 따로 만드는 게 원칙이다. 라이브러리 충돌을 막는다.
nvidia-smi # CUDA 확인
conda create -n [이름] python=[버전]
conda activate [이름]
conda env list
conda install jupyter notebook
conda install pytorch torchvision torchaudio pytorch-cuda=11.8 -c pytorch -c nvidia
python -m ipykernel install --user --name 가상환경이름 --display-name 표시이름
마지막 줄을 빼먹으면 주피터에서 그 가상환경이 커널 목록에 안 뜬다. 환경은 만들었는데 노트북에서 못 고르는 상황이 여기서 나온다.
PyTorch 구조
| 모듈 | |
|---|---|
torch.Tensor | 기본 데이터 구조 |
torch.nn | 신경망 모듈 |
torch.optim | 최적화 알고리즘 (SGD, Adam) |
torch.utils.data | 데이터셋과 데이터로더 |
torch.autograd | 자동 미분 |
torch.device | GPU(cuda) / CPU(cpu) |
사전학습 모델로 추론하는 흐름은 정해져 있다.
import torch
import torchvision.models as models
import torchvision.transforms as transforms
model = models.alexnet(pretrained=True)
model.to(device)
model.eval()
model.to(device) 와 model.eval() 이 둘 다 필요하다.
to(device)— 모델과 입력 텐서가 같은 디바이스에 있어야 한다. 하나만 GPU 로 보내면 런타임 에러가 난다eval()— Dropout 과 BatchNorm 을 평가 모드로 바꾼다. 안 하면 추론할 때마다 결과가 달라진다
전처리는 학습 때와 완전히 같아야 한다.
transform = transforms.Compose([
transforms.Resize(256), # 짧은 쪽을 256 으로
transforms.CenterCrop(224), # 가운데 224×224 (AlexNet 입력 규격)
transforms.ToTensor(), # H,W,C → C,H,W 변환 포함
transforms.Normalize(mean, std), # ImageNet 학습 때의 평균·표준편차
])
input_tensor = transform(img).unsqueeze(0) # 배치 차원 하나 추가
with torch.no_grad(): # 기울기 계산 끄기 — 메모리·속도 절약
output = model(input_tensor)
_, pred = torch.max(output, 1) # 가장 점수 높은 클래스
unsqueeze(0) 이 자주 걸린다. 모델은 항상 배치 단위를 기대하는데 이미지 한 장은
(C, H, W) 라 차원이 하나 모자라다. (1, C, H, W) 로 만들어야 들어간다.
Normalize 의 평균·표준편차도 마음대로 정하면 안 된다. 사전학습 때 쓴 그 값을 그대로
써야 한다. 이게 다르면 모델은 멀쩡한데 예측이 엉망으로 나온다.
엣지에서 추론하기 — 라즈베리파이
source myenv/bin/activate
python rpi_pytorch_imageclassification.py
| 라이브러리 | 역할 |
|---|---|
torch, torchvision | 모델 로딩과 추론 |
transforms | 이미지 전처리 |
picamera2 | 라즈베리파이 카메라 모듈로 영상 캡처 |
cv2 | OpenCV |
PIL.Image | 이미지 포맷 변환 |
picamera2 로 찍고 → PIL 로 포맷을 맞추고 → transforms 로 전처리하고 → 모델에 넣는다.
카메라가 주는 형식과 모델이 원하는 형식이 다르기 때문에 중간에 변환 단계가 반드시 낀다.
웹서버
| 구분 | 하는 일 |
|---|---|
| 요청 처리 Request | HTTP 요청(GET, POST)을 받아 경로에 따라 파일 반환 또는 애플리케이션 서버 연동 |
| 응답 생성 Response | HTML·JSON·이미지·비디오 반환. 상태 코드(200 OK, 404 Not Found) 포함 |
| 정적 콘텐츠 | HTML/CSS/JS/이미지를 그대로 제공. 빠르고 CPU 를 거의 안 쓴다 |
| 동적 콘텐츠 | 입력·조건에 따라 서버 측 코드를 실행해 결과 생성 |
| 로깅과 보안 | 요청·응답 기록, HTTPS, 인증, IP 차단 |
| 정적 웹서버 | 동적 웹서버 | |
|---|---|---|
| 콘텐츠 | 고정된 파일 | 실시간 생성 |
| 예시 | HTML, CSS, 이미지 | DB 와 연동된 HTML, JSON |
| 처리 | 파일 그대로 제공 | 서버 측 코드 실행 |
| 사용 예 | 정적 페이지, 포트폴리오 | 로그인, 검색, 게시판 |
| 웹서버 | 특징 |
|---|---|
| Apache HTTP | 가장 오래되고 널리 쓰인다. 모듈 기반으로 기능 추가가 자유롭다 |
| NGINX | 이벤트 기반 아키텍처. 정적 파일 처리에 뛰어나고 메모리를 적게 쓴다 |
| Node.js | JavaScript 기반. RESTful API 나 실시간 채팅 같은 비동기 서비스에 적합 |
| Flask | 간단한 라우팅과 API. 웹 애플리케이션·백엔드로 널리 쓰인다 |
비동기는 작업이 끝나기를 기다리지 않고 다음 작업으로 넘어가는 방식이다. 센서 수백 개가 동시에 데이터를 밀어 넣는 IoT 환경에서 이 차이가 크게 벌어진다.
Flask
| 특징 | |
|---|---|
| 경량 | 기본 기능만 제공해 빠르고 단순 |
| 확장성 | ORM·인증·세션 관리를 필요할 때 붙인다 |
| 라우팅 | URL 경로에 함수를 매핑해 직관적 |
| Python 기반 | 파이썬 코드로 그대로 서버 제어 |
| 템플릿 | Jinja2 로 HTML 생성 |
라우팅은 사용자가 특정 URL 에 접근했을 때 어떤 함수가 실행될지를 정의하는 개념이다.
pip install flask
Project
|-- app.py # Flask 서버 코드
|-- templates/ # HTML 템플릿
`-- static/ # 정적 파일 (CSS, JS, 이미지)
폴더 이름이 정해져 있다. templates 와 static 은 Flask 가 그 이름으로 찾기 때문에
바꾸면 안 된다.
Jinja2 문법은 네 개면 충분하다.
| 기능 | 문법 |
|---|---|
| 변수 출력 | {{ 변수명 }} |
| if 문 | {% if 조건 %} 내용 {% endif %} |
| for 문 | {% for item in 리스트 %} 내용 {% endfor %} |
| 주석 | {# 주석 #} |
HTML 태그도 최소한만 알면 된다 — <html> 루트, <head> 메타 정보, <title> 탭 제목,
<body> 실제 내용, <h1>~<h6> 제목(숫자가 작을수록 크다), <p> 단락,
<a href="url"> 링크, <img src="경로" alt="설명"> 이미지.
MQTT
Message Queuing Telemetry Transport — 저대역폭 환경에서 IoT 원격 측정을 위해 설계된 경량 메시지 전송 프로토콜이다. ISO/IEC 20922 표준이다.
헤더가 작게는 단 2바이트다. 이 한 줄이 MQTT 를 쓰는 이유 전부다. HTTP 헤더가 수백 바이트인 걸 생각하면, 배터리로 도는 센서가 하루에 수천 번 보내야 할 때 차이가 크다.
구성
| 구성 요소 | 역할 |
|---|---|
| Broker | 중앙 서버. 메시지 송수신 중계 |
| Publisher | 데이터를 보내는 장치 (예: 센서) |
| Subscriber | 데이터를 받는 장치 (예: 웹서버) |
Publisher 와 Subscriber 는 서로를 모른다. 토픽만 알면 된다. 이 느슨한 결합이 장치를 추가하거나 빼기 쉽게 만든다.
| 소프트웨어 | 역할 |
|---|---|
| Mosquitto | Broker. Publisher 와 Subscriber 사이의 통신 관리 |
| Paho MQTT | 클라이언트. 파이썬으로 MQTT 구현 |
sudo apt install mosquitto mosquitto-clients
sudo systemctl enable mosquitto
sudo systemctl start mosquitto
mosquitto_pub -h localhost -t "test/topic" -m "hello mqtt"
mosquitto_sub -h localhost -t "test/topic"
enable 은 부팅할 때 자동 시작, start 는 지금 시작이다. 둘 다 해야 재부팅 후에도 산다.
QoS
메시지가 수신자에게 도달하는 보장 수준을 정의한다.
| QoS | 전송 보장 | 손실 위험 | 중복 위험 | 쓰는 곳 |
|---|---|---|---|---|
| 0 | 최대 한 번 | 있음 | 없음 | 센서 데이터(모니터링) 등 중요도 낮은 메시지 |
| 1 | 최소 한 번 | 없음 | 있음 | 제어 신호 등 중요도 높은 메시지 |
| 2 | 정확히 한 번 | 없음 | 없음 | 금융·거래 정보 등 중복되면 안 되는 메시지 |
QoS 1 은 중복이 온다는 게 핵심이다. “받았다”는 확인이 유실되면 다시 보내기 때문이다. 그래서 QoS 1 로 제어 신호를 보낼 때는 같은 명령을 두 번 받아도 안전하도록 수신 쪽을 설계해야 한다. QoS 2 는 이걸 프로토콜이 해결해 주는 대신 왕복이 늘어 느리다.
시계열
IoT 데이터는 대부분 시계열이다. 그래서 마지막 부분이 시계열이었다.
| 시계열 데이터 | 비시계열 데이터 | |
|---|---|---|
| 정의 | 시간 흐름에 따라 순차 기록 | 시간 순서가 중요하지 않음 |
| 시간 의존성 | 순서와 관계가 중요 (과거→현재→미래) | 순서를 바꿔도 의미 불변 |
| 특징 | 자기상관 존재, 계절성·추세 분석 가능 | 독립적인 레코드, 통계적 독립 가정 |
| 구조 | (timestamp, value), 종종 다변량 | 테이블. 각 행이 독립 샘플 |
| 분석 목적 | 예측, 이상 탐지, 패턴 분석 | 분류, 회귀, 군집화 |
| 기법 | ARIMA, SARIMA, Holt-Winters / RNN, LSTM, GRU, Transformer | 로지스틱 회귀, SVM, 의사결정나무 / CNN, MLP, XGBoost |
분해와 정상성
시계열은 추세 + 계절 + 오차로 나눈다.
| 성분 | |
|---|---|
| 추세 Trend | 장기적인 증가·감소 |
| 계절성 Seasonality | 주기적으로 반복 |
| 순환 Cycle | 일정하지 않지만 반복되는 파동 |
| 불규칙성 Noise | 예측 불가능한 요동 |
계절성과 순환의 차이가 시험에 나올 만하다 — 주기가 일정하면 계절성, 일정하지 않으면 순환이다.
정상성은 시간이 지나도 평균·분산·자기공분산이 일정하게 유지되는 성질이다.
| 조건 | |
|---|---|
| 평균 불변 | 평균이 시간에 따라 일정 |
| 분산 불변 | 분산이 시간에 따라 일정 |
| 시차에만 의존하는 자기공분산 | 패턴이 시간에 따라 바뀌지 않는다 |
비정상 시계열을 정상화해야 하는 이유는 대부분의 통계적 예측 모델이 정상성을 전제하기 때문이다. 평균이 계속 변하는 데이터에서는 “평균으로 돌아온다”는 예측 자체가 성립하지 않는다.
모델
| 하는 일 | |
|---|---|
| 이동평균 MA | SMA 는 고정 윈도우 평균, EMA 는 최근 데이터에 더 큰 가중치. 노이즈 제거·추세 파악 |
| 자기회귀 AR | 과거 자신의 값들로 현재를 예측 |
| 항목 | 전통 통계 (ARIMA 등) | 딥러닝 (LSTM 등) |
|---|---|---|
| 가정 | 정상성을 전제 | 데이터에서 패턴 학습 |
| 입력 | 시계열 수치 그대로 | 수치 그대로 또는 피처 엔지니어링 |
| 유연성 | 제한적 (선형·시차 기반) | 매우 유연 (비선형, 긴 시계열) |
| 정상성 | 필요 | 불필요하나 있으면 도움 |
딥러닝 계열은 이렇게 발전했다.
- RNN — 과거 정보를 기억해 순서 있는 데이터를 처리. 한계는 기억이 흐려지는 것(기울기 소실)
- LSTM — 입력·망각·출력 게이트로 오래된 정보도 유지
- GRU — LSTM 의 간소화. 기억 셀이 없고 업데이트·리셋 게이트만. 매개변수가 적다
- 1D CNN — 병렬 처리 가능. 모델이 단순하고 빠르며 짧은 구간 패턴을 효율적으로 뽑는다
Transformer 계열
“중요한 부분”에 집중해서 이해하는 방법을 가진 모델이다. 단어가 다른 단어와 얼마나 관련 있는지를 계산하고(attention), 장기 의존성 학습을 위해 자기 주의 메커니즘을 쓴다.
| 모델 | 핵심 아이디어 |
|---|---|
| Transformer | Self-Attention 기반 병렬 시계열 처리 |
| Informer | Sparse Attention 으로 연산 복잡도를 낮춰 긴 시퀀스 처리. 계층 간 정보 요약 |
| Autoformer | 시계열을 Trend + Seasonal 로 분해해 처리. 장기 예측에서 안정적 |
| PatchTST | 시계열을 이미지처럼 patch 단위로 나눠 Transformer 에 입력. 학습이 빠르다 |
PatchTST 를 따로 정리해 두었다.
- 패치 단위로 처리해 전역적 시점 정보를 더 효과적으로 포착한다
- 자기회귀 구조를 쓰지 않고 한 번에 다단계 예측을 수행한다
- Conv 나 RNN 계열 없이도 강력한 성능을 낸다
- 장기 의존성과 다변량 예측을 동시에 잘한 첫 Forecasting 모델이다
자기회귀 구조를 버렸다는 게 실용적으로 크다. 한 스텝씩 예측해 다시 입력으로 넣으면 오차가 누적되는데, 다단계를 한 번에 내면 그 문제가 없다.