iT邦幫忙

2025 iThome 鐵人賽

DAY 26
0
AI & Data

從0開始的MLFLOW應用搭建系列 第 26

Day 26 – 用 Streamlit 打造動畫推薦系統介面

  • 分享至 

  • xImage
  •  

🎯 本日目標

今天,我們要讓整個推薦系統從後端 API,
正式長出一個「可互動的前端」!

透過 Streamlit,我們將實作一個簡潔、即時互動的 UI,
使用者可以輸入暱稱、挑選喜歡的動畫,
系統會呼叫 FastAPI /recommend
並以條列清單輸出推薦結果。


🧩 系統架構概覽

整體結構如下圖所示:

📦 anime-recsys/
 ┣ 📁 docker/                      # Docker 設定
 ┃ ┣ Dockerfile.streamlit
 ┃ ┗ requirements-streamlit.txt
 ┣ 📁 notebooks/                   # 放置資料集 (anime_clean.csv)
 ┃ ┗ data/anime_clean.csv
 ┣ 📁 src/api/                     # FastAPI + Streamlit 應用程式
 ┃ ┣ main.py                       # FastAPI 主程式
 ┃ ┗ main_streamlit.py             # Streamlit 主程式
 ┣ docker-compose.yml
 ┗ mlruns/                         # MLflow artifact 儲存區

📦 Step 1. requirements-streamlit.txt

建立 docker/requirements-streamlit.txt

streamlit==1.38.0
requests==2.32.3
pandas==2.2.2

🐳 Step 2. Dockerfile.streamlit

建立 docker/Dockerfile.streamlit

FROM python:3.10-slim

WORKDIR /src/api
COPY docker/requirements-streamlit.txt .

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

此 Dockerfile 僅負責安裝 Streamlit 所需環境,
指令與程式的啟動交由 docker-compose 控制。


⚙️ Step 3. docker-compose.yml

在現有 MLflow + FastAPI 架構中,加入新的 streamlit 服務:

  streamlit:
    build:
      context: .
      dockerfile: docker/Dockerfile.streamlit
    container_name: streamlit_app
    ports:
      - "8501:8501"
    environment:
      - FASTAPI_URL=http://fastapi:8000
    volumes:
      - ./src/api:/src/api
      - ./notebooks:/src/api/notebooks
    working_dir: /src/api
    command: >
      streamlit run main_streamlit.py
      --server.port=8501
      --server.address=0.0.0.0
      --server.runOnSave=true
    depends_on:
      - fastapi
    networks:
      - mlops-net

🔸 streamlit 使用本地掛載 ./notebooks
以讀取 anime_clean.csv


🧠 Step 4. main_streamlit.py

建立 /src/api/main_streamlit.py


import os
import pandas as pd
import streamlit as st
import requests

FASTAPI_URL = os.getenv("FASTAPI_URL", "http://localhost:8000")
ANIME_CSV_PATH = "/src/api/notebooks/data/anime_clean.csv"  # 固定資料路徑

st.set_page_config(page_title="🎬 Anime Recommender", layout="wide")

st.title("🎬 Anime Recommendation System")

st.markdown("""
這是一個基於 **MLflow + FastAPI** 的推薦系統展示 🎯  
請輸入暱稱並選擇你喜歡的動畫,我們會為你推薦相似作品!
""")

# --- Step 1. 使用者輸入暱稱 ---
nickname = st.text_input("請輸入你的暱稱 👤", placeholder="例如:Josh、小智、NekoFan")
if not nickname:
    st.info("請輸入暱稱後再繼續。")

# --- Step 2. 載入動畫清單 ---
@st.cache_data
def load_anime_list():
    if not os.path.exists(ANIME_CSV_PATH):
        st.error(f"❌ 找不到資料檔案:{ANIME_CSV_PATH}")
        return []
    try:
        df = pd.read_csv(ANIME_CSV_PATH)
        if "name" not in df.columns:
            st.error("❌ anime_clean.csv 缺少 'name' 欄位。請確認資料格式。")
            return []
        return df["name"].dropna().unique().tolist()
    except Exception as e:
        st.error(f"❌ 讀取 anime_clean.csv 失敗:{e}")
        return []


anime_list = load_anime_list()

# --- Step 3. 使用者選擇動畫 ---
selected_anime = st.multiselect(
    "選擇你喜歡的動畫(最多5部) 🎥", 
    anime_list, 
    max_selections=5,
    placeholder="例如:Naruto、Bleach、Attack on Titan..."
)

# --- Step 4. 按下推薦按鈕 ---
if st.button("🚀 取得推薦結果"):
    if not nickname:
        st.warning("請先輸入暱稱。")
    elif not selected_anime:
        st.warning("請至少選擇一部動畫。")
    else:
        payload = {
            "user_id": nickname,           # ✅ 使用暱稱當 user_id
            "anime_titles": selected_anime
        }
        params = {"model_name": "AnimeRecsysModel"}

        try:
            res = requests.post(f"{FASTAPI_URL}/recommend", json=payload, params=params)
            if res.status_code == 200:
                data = res.json()
                recs = data.get("recommendations", [])

                if not recs:
                    st.warning("⚠️ 沒有推薦結果,請確認模型是否已正確部署。")
                else:
                    st.markdown("---")
                    st.subheader(f"✨ 為 **{nickname}** 推薦的動畫:")
                    for i, title in enumerate(recs[:10], 1):
                        st.markdown(f"{i}. **{title}**")  # ✅ 條列式輸出
            else:
                st.error(f"❌ 無法取得推薦結果:{res.text}")
        except requests.exceptions.RequestException as e:
            st.error(f"❌ 無法連線至 FastAPI:{e}")

🚀 Step 5. 啟動與測試

1️⃣ 確保 anime_clean.csv 位於:

notebooks/data/anime_clean.csv

2️⃣ 啟動專案:

docker-compose up --build

3️⃣ 開啟瀏覽器:

4️⃣ 在 UI 中:

  • 輸入暱稱
  • 選擇喜歡的動畫
  • 按下「🚀 取得推薦結果」

即可看到:

https://ithelp.ithome.com.tw/upload/images/20251010/20178626Ngx3GmjOGm.png


✅ 今日重點

主題 說明
🧩 Streamlit 建立互動式推薦介面
👤 輸入暱稱 暱稱作為 user_id 傳入 API
🎥 多選動畫 使用 multiselect 輸入喜好
🧠 推薦結果 條列式顯示推薦清單
🐳 Docker 一鍵啟動 完整整合 MLflow + FastAPI + Streamlit

🎉 Day 26 完成!
我們成功打造出一個可互動的推薦系統前端。
明天(Day 27),我們將進一步串接 /log-ab-event
記錄使用者點擊與行為,進入 A/B 測試與行為追蹤階段


上一篇
Day 25 – 建立第二個模型並實作 A/B 測試分流機制
下一篇
Day 27 – 建立 A/B Test 資料收集機制
系列文
從0開始的MLFLOW應用搭建29
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言