카테고리 없음

시계열 모델링과 MLops 적용하기 2

iron-min 2025. 11. 27. 20:08

1. 오늘 배운것.

 

1.MLops 의 이해

1-1. MLops 개념 정리

 

  • 기존에 DevOps(데브옵스)는 개발(Development)와 운영(Operations)이 합쳐진 용어
  • 개발과 운영의 경계를 허물고 협업하여 아이디어를 개발하고 배포하는 개발 환경과 도구
  • 이를 ML시스템에 적용한 것이 MLops

MLOps는 머신 러닝 작업(Machine Learning Operations)을 뜻합니다. MLOps는 머신 러닝 모델을 프로덕션으로 전환하는 프로세스를 간소화하고, 뒤이어 이를 유지관리하고 모니터링하는 데 주안점을 둔 머신 러닝 엔지니어링의 핵심 기능입니다. MLOps는 협업 기능이며, 주로 데이터 사이언티스트, DevOps 엔지니어, IT로 구성됩니다.

 

 

MLops의 조건

  • 지속적 통합(Continuous Intergration, CI)
    • 코드의 변경사항을 정기적으로 빌드 및 테스트하고 공유 리포지토리에 통합
  • 지속적 배포/제공(Continuous Deployment/Delivery, CD)
    • 파이프라인, 모델 등 예측 서비스를 자동으로 배포
  • 지속적 학습(Continuous Training, CT)
    • 데이터가 업데이트 될 때 마다 자동으로 학습 및 업데이트

 

 

2.모델 서빙 준비하기

 

✅ 서빙 방법

  • 모델 학습이 완료되었으면 결과를 활용할 수 있도록 해야함

 

 

방법1. ML prediction 결과를 database에 저장한다.

 

 

 

 

방법2. API를 사용하여 필요한 서비스가 호출해서 쓸 수 있도록 만듦

 

 

 

  • 보통 산업에서는 GCP, AWS와 같은 SaaS(Software as a Service)를 이용하여 개발함
  • Linux 운영체제를 활용하는 것이 기본

 

  리눅스란?

 

  • Linux는 1991년 Linus Torvals가 Unix 운영체제 기반으로 개발한 무료 소프트웨어 운영체제
  • 윈도우, 맥 OS가 있기 전의 운영체제의 조상
  • 현재 전세계적으로 300여가지의 배포판이 존재하여, 사용자에 따라 결정할 수 있는 폭이 넓습니다. (ex 개인용컴퓨터, 모바일)
  • 여러분들이 사용하는 PC 외에 대부분 SaaS 서비스가 리눅스 기반입니다. (Colab도 !)
  • 배포판 분류
    • 회사에서 관리: 레드햇, 우분투
    • 커뮤너티 관리: 데비안, 젠투, 페도라

 

 

 2-1.백엔드와 프론트엔드

  • Backend: 보이지 않는 곳에서 핵심 로직을 처리
    • 데이터 수집, 연산, DB에 데이터 저장 등
  • Frontend: 사용자 눈에 보이는 화면
    • 사용자가 마우스로 누르는 화면

 

 

 

  웹에서 더불어 등장하는 개념

  • 모놀리식 아키텍쳐(Monolithic Architecture): 모든 소프트웨어의 구성요소가 한 프로젝트에 통합
    • 소규모 프로젝트나 운영에는 적합하나, 일부 오류에도 전체 영향을 미칠 수 있음
  • 마이크로서비스 아키텍쳐(MicroService Architecture, MSA): 여러개의 작은 서비스 유닛을 쪼개어 운영
  • IP(Internte Protocol): 컴퓨터(서버)의 주소에 해당. 127.0.0.1 혹은 localhost는 내 컴퓨터를 가르키는 주소
  • Port(포트): 한 서버 내에서의 호수(Room Number)
    • 한 컴퓨터에서는 여러 프로그램이 동시에 인터넷을 사용하기 때문에 Port 단위로 나눔
    • Ex) 8080: FastAPI, 8501: Streamlit

 

 

 

2-2.실습 - FastAPI + Streamlit 조합으로 만들기

 

1. 가상환경 생성

python -m venv myenv

# 가상환경 활성화
# Windows
myenv\Scripts\activate
# macOS/Linux
source myenv/bin/activate

 

2. 백엔드 코드 작성 및 실행

 

 1) backend/main.py  코드 생성

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import random
import math
import time
from datetime import datetime
from collections import deque
import numpy as np
from sklearn.linear_model import LinearRegression # <-- ML 모델 소환

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# --- [In-Memory Database] ---
# 최근 30개의 데이터만 기억하는 저장소 (Queue)
# 모델이 학습할 '데이터셋' 역할을 합니다.
data_buffer = deque(maxlen=30) 

def generate_fake_temperature():
    now = time.time()
    base_temp = 65.0
    cycle = 10 * math.sin(now / 10)
    noise = random.uniform(-1.5, 1.5)
    return round(base_temp + cycle + noise, 2)

@app.get("/")
def health_check():
    return {"status": "ok"}

@app.get("/current-temp")
def get_current_temp():
    temp = generate_fake_temperature()
    
    # [핵심] 생성된 데이터를 버리지 않고 '학습 데이터'로 저장합니다.
    data_buffer.append(temp)
    
    return {
        "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        "temperature": temp,
        "status": "DANGER" if temp > 72 else "NORMAL"
    }

@app.get("/predict-temp")
def get_prediction():
    """
    [Real ML Model Serving]
    저장된 최근 데이터(data_buffer)를 사용하여 
    선형 회귀 모델을 '실시간으로' 학습시키고 미래를 예측합니다.
    """
    
    # 1. 학습 데이터가 충분한지 확인 (최소 10개 이상)
    if len(data_buffer) < 10:
        return {"forecast": [], "message": "데이터 수집 중... (최소 10개 필요)"}

    # 2. 데이터 전처리 (Scikit-Learn이 좋아하는 형태로 변환)
    # X: 시간(인덱스) [0, 1, 2, ... N]
    # y: 온도 값
    y = np.array(data_buffer)
    X = np.arange(len(y)).reshape(-1, 1)

    # 3. 모델 학습 (Training)
    # "최근 데이터의 추세(기울기)를 배워라"
    model = LinearRegression()
    model.fit(X, y)

    # 4. 미래 예측 (Inference)
    # 현재 시점 이후 10스텝(Future X)을 만듭니다.
    next_steps = np.arange(len(y), len(y) + 10).reshape(-1, 1)
    predictions = model.predict(next_steps)

    return {"forecast": np.round(predictions, 2).tolist()}

 

 2) 터미널 1: Backend 실행

# 필요한 라이브러리 설치
pip install fastapi uvicorn scikit-learn numpy

# backend 폴더로 이동 후 실행
cd backend
uvicorn main:app --reload --port 8000

 

3. 프론트 엔드

 1) frontend/app.py 코드 생성

import streamlit as st
import requests
import pandas as pd
import time
import plotly.express as px

# --- [설정] ---
# Docker Compose 배포 시에는 'http://backend:8000'으로 변경해야 함
API_URL = "127.0.0.1:8000"

st.set_page_config(page_title="공장 온도 모니터링", layout="wide")

st.title("🏭 실시간 사출 성형기 온도 관제 시스템")
st.markdown("---")

# 레이아웃 구성 (2단)
col1, col2 = st.columns([1, 2])

# 데이터를 저장할 공간 (세션 스테이트 활용)
if "history" not in st.session_state:
    st.session_state["history"] = []

# --- [메인 루프] ---
# Streamlit을 실시간 대시보드처럼 쓰기 위한 placeholder
placeholder = st.empty()

# 'Stop' 버튼을 누르기 전까지 계속 갱신
if st.button('모니터링 시작/중지'):
    while True:
        try:
            # 1. FastAPI에서 데이터 가져오기
            res_current = requests.get(f"{API_URL}/current-temp")
            res_predict = requests.get(f"{API_URL}/predict-temp")
            

            if res_current.status_code == 200 and res_predict.status_code == 200:
                data = res_current.json()
                preds = res_predict.json().get('forecast', []) # 혹시 키가 없어도 에러 안 나게 get 사용
                
                curr_temp = data['temperature']
                curr_time = data['timestamp']
                status = data['status']

                # 데이터 누적
                st.session_state["history"].append({"time": curr_time, "temp": curr_temp})
                if len(st.session_state["history"]) > 30:
                    st.session_state["history"].pop(0)
                
                df = pd.DataFrame(st.session_state["history"])

                # --- [화면 그리기 수정됨] ---
                with placeholder.container():
                    # (1) 상단 지표 (Metric)
                    m_col1, m_col2, m_col3 = st.columns(3)

                    # 예측 데이터가 있을 때만 계산
                    if len(preds) > 0:
                        pred_msg = f"{preds[-1]} °C"
                        delta_msg = f"{preds[0] - curr_temp:.1f} 예상"
                    else:
                        pred_msg = "학습 데이터 수집 중..."
                        delta_msg = "0"

                    m_col1.metric(label="현재 설비 온도", value=f"{curr_temp} °C", delta=delta_msg)
                    m_col2.metric(label="상태", value=status, delta_color="inverse" if status == "DANGER" else "normal")
                    m_col3.metric(label="모델 예측(10분 후)", value=pred_msg)

                    # (2) 경고 메시지
                    if status == "DANGER":
                        st.error("🚨 [경고] 설비 온도가 임계치를 초과했습니다! 냉각수를 확인하세요.")
                    else:
                        st.success("✅ 설비가 정상 가동 중입니다.")

                    # (3) 차트 시각화
                    fig = px.line(df, x='time', y='temp', title='실시간 온도 추이', markers=True)
                    
                    # 예측값이 있다면 차트에 덧그리기 (선택사항)
                    if len(preds) > 0:
                        # 미래 시간축 생성 (간단히 구현)
                        # 현재 마지막 시간에서 1분씩 더한다고 가정 등 시각화 로직 추가 가능
                        pass 

                    st.plotly_chart(fig, use_container_width=True)

            else:
                st.error("백엔드 서버와 통신 실패")

        except Exception as e:
            st.error(f"연결 오류: {e}")
            # 백엔드가 안 켜져있을 때 계속 재시도하지 않도록 잠시 대기
            time.sleep(1)

        # 1초마다 갱신
        time.sleep(1)

 

 2) 터미널 2:Frontend 실행

# 필요한 라이브러리 설치
pip install streamlit requests pandas plotly

# frontend 폴더로 이동 후 실행
cd frontend
streamlit run app.py

 

 

4. 결과확인 : http://localhost:8501/

 

3.도커 설정하기

3-1. 도커의 필요성과 개념

  • 가상화의 필요성
    • 새로운 운영체제에 시스템을 설치하고자 한다면 새로운 컴퓨터를 구입해야할까? → No. 운영체제 가상화를 통해 해결. Docker 사용
    • 독립적인 파이썬 환경을 구축하고 싶다면? → 가상환경 구축을 통해 서로 다른 프로젝트 간 독립성을 확보. venv 사용
  • Linux, Window, Mac 등 운영체제가 다양하고 사용하는 명령어 기술이 다름.
  • 때문에 로컬 개발마다 재현성 안 나올 수 도 있다. Docker를 이용하면 동일한 개발 환경에서 작업이 가능한 장점
  • Docker Hub에서 공식으로 **도커 이미지(Docker Official Image)**를 제공하여 쉽게 도커 이미지를 가져올 수 있음
  • 도커이미지: 어떤 어플리케이션에 대하서 단순히 어플리케이선 코드 뿐 아니라, 의존성이 있는 프로그래들을 함께 패키징하여 어느 환경에서든지 실행해볼 수 있도록 만든 데이터

 

 

3-2. 실습

더보기

백엔드, 프론트 엔드는 하나의 서버에 합칠 수 있습니다. 이걸 모놀리식 구성이라고 하고, 각 기능만큼의 서버로 나눠서 설계하는 것을 MSA 식이라고 합니다. 제조업이 환경은 매우 서버와 데이터가 많기때문에 이를 가정하여 각각 컨테이너화를 하는 것이 권장됩니다.

1) requirements.txt 생성

● backend/requirements.txt

fastapi
uvicorn
scikit-learn
numpy

 

● frontend/ requirements.txt

streamlit
requests
pandas
plotly

 

2) Dockerfile 작성

● backend/Dockerfile

# 1. 가볍고 안정적인 파이썬 3.9 버전(Slim)을 베이스로 씁니다.
FROM python:3.9-slim

# 2. 작업 폴더를 만듭니다.
WORKDIR /app

# 3. 필요한 라이브러리 명세서를 복사하고 설치합니다.
# (캐싱 효과를 위해 requirements를 먼저 복사하는 것이 국룰입니다)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 4. 나머지 소스 코드를 다 복사합니다.
COPY . .

# 5. 컨테이너가 켜질 때 실행할 명령어입니다.
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

 

● frontend/Dockerfile

FROM python:3.9-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

# Streamlit 기본 포트인 8501번을 열고 실행합니다.
CMD ["streamlit", "run", "app.py", "--server.port", "8501", "--server.address", "0.0.0.0"]

 

3. ★(중요) frontend/app.py 에서 API_URL을 변경

더보기

로컬에서는 localhost가 노트북이지만 컨테이너화된 Docker에서는 더이상 localhost가 아닙니다. 따라서 frontend 입장에서 데이터를 찾기위한 직접적인 API_URL를 전달해줄 필요가 있습니다.

# [수정 전]
# API_URL = "http://127.0.0.1:8000"

# [수정 후] Docker 환경용
# 도커 컴포즈가 내부 DNS로 이름을 연결해줍니다.
API_URL = "http://backend-service:8000"

 

4. docker-compose.yml 파일 생성

● 단일 docker가 아닌 여러개의 docker를 올리는 경우 docker-compose 를 활용

version: '3.8'

services:
  # 1번 컨테이너: 백엔드
  backend-service:
    build: ./backend        # backend 폴더에 있는 Dockerfile로 빌드해라
    container_name: factory-backend
    ports:
      - "8000:8000"         # 내 컴퓨터 8000번 <-> 컨테이너 8000번 연결

  # 2번 컨테이너: 프론트엔드
  frontend-service:
    build: ./frontend       # frontend 폴더에 있는 Dockerfile로 빌드해라
    container_name: factory-dashboard
    ports:
      - "8501:8501"         # 내 컴퓨터 8501번 <-> 컨테이너 8501번 연결
    depends_on:
      - backend-service     # 백엔드가 켜진 다음에 프론트엔드를 켜라 (순서 보장)

 

5.실행 및 결과 확인

● docker - compose up --build

 

 

 

 

2. 느낀점.

 

너무어려워서 하나도 이해를 못한것 같습니다....

나중에 자료찾아가 보면서 다시공부해야 할것 같습니다.

정리하면서도 이렇게 이해 못하는 경우는 처음인것 같습니다.