模型要有資料才能訓練。而資料在你的筆電、在資料庫、在某個 NAS 上—— 怎麼弄到平台上,讓 pipeline 和 workbench 讀得到?

四種方式,然後是一個更重要的問題。


四種方式

① 手動上傳到 S3(最快,也最沒紀錄)

mc cp corpus.txt local/models/llm/clean_corpus.txt

三十秒搞定,適合 lab 和第一次試。 我自己就是這樣放的——而這篇後半就是在講它的代價。

② 從 workbench 拉

在 Jupyter 裡直接抓:

import boto3
s3 = boto3.client("s3", endpoint_url=os.environ["AWS_S3_ENDPOINT"])
s3.download_file("models", "llm/corpus.txt", "/opt/app-root/src/corpus.txt")

憑證來自你掛上去的 Connection不用寫死在程式裡

適合探索階段。缺點是那個過程留在某個人的 notebook 裡—— 別人重現不了,而且 notebook 常常不進版控。

③ 用 pipeline 的一棒去抓(推薦)

把「取得資料」變成 pipeline 的第一棒,跟訓練、評估同一條鏈:

@dsl.container_component
def ingest_data(source_url: str, run_id: str):
    return dsl.ContainerSpec(image=IMAGE, command=["bash","-c", INGEST], args=[source_url, run_id])

好處是它會留下紀錄:哪一次 run、用了什麼參數、產出放哪。 這是唯一能讓「資料怎麼來的」變成可查詢事實的做法。

④ 直接掛外部儲存(PVC / NFS / CSI)

資料太大、或本來就在企業儲存上時用這個。

⚠️ 但它有一個治理問題:掛進來的東西不受平台版本控制。 NFS 上那個檔案被別人改了,你的訓練結果就變了,而且沒有任何紀錄。


⭐ 但真正的問題不是「怎麼放」

我去查了自己平台上那份訓練語料,能查到的全部資訊只有這些

mc stat local/models/llm/clean_corpus.txt
Name      : clean_corpus.txt
Date      : 2026-08-24 03:35:32 UTC
Size      : 99 MiB
ETag      : 9e4a738f546d71860ae83a1ac165112f-7
Metadata  :
  Content-Type: text/plain

檔名、日期、大小、ETag。就這樣。

查不到的東西:

問題 答案
這是什麼資料? ?(檔名叫 clean_corpus,那是處理後的名字)
從哪裡來的?
什麼時候抓的? ?(8/24 是上傳時間,不是抓取時間)
用什麼腳本處理的?哪一版?
授權是什麼?可以拿來訓練嗎?
誰放上去的?

而這份資料訓練出來的模型,現在正在對外服務。


為什麼這件事比它看起來嚴重

模型會把資料的一切學進去——包括你不知道自己收了什麼。

出事的時候,你會被問這些:

  • 「這個模型講出了不該講的東西,訓練資料裡有什麼?」
  • 「這批資料的授權允許商業使用嗎?」
  • 「有個人資料在裡面嗎?」
  • 「重跑一次能不能得到同一顆模型?」

這四題都不是模型的問題,是資料的問題。 而如果你的資料只有「檔名、日期、大小」,你一題都答不出來。

⚠️ 更麻煩的是:這些問題通常在你最不想回答的時候被問到。


補法:給資料一張身分證

跟資料一起放一個 dataset_card.json

{
  "name": "zhwiki-clean",
  "source_url": "https://dumps.wikimedia.org/zhwiki/20260601/...",
  "downloaded_at": "2026-06-21T08:05:00+08:00",
  "raw_sha256": "sha256:...",
  "raw_size_bytes": 110234567,
  "license": "CC BY-SA 4.0",
  "processing": {
    "script": "pipeline/01_prepare_data.py",
    "code_commit": "2d4f691",
    "steps": ["collect", "clean", "quality-filter", "exact-dedup", "near-dedup"],
    "dropped": {"quality": 12, "exact": 0, "near": 161}
  },
  "output_sha256": "sha256:...",
  "docs_out": 11126,
  "chars": 39850979
}

六個欄位是最低限度

欄位 為什麼要
source_url 出事時要能回到源頭
downloaded_at 資料有時效性;上傳時間 ≠ 抓取時間
raw_sha256 證明「我抓的就是那一份」
license 法遵。這一欄沒有,前面五欄都沒意義
code_commit 同一份原始資料 + 不同版本的清理腳本 = 不同的訓練資料
output_sha256 讓模型的 lineage 指得到這一份

然後把 output_sha256 寫進模型台帳的 lineage。 這樣「線上這顆模型用了哪份資料」就是一個可查詢的事實,不是一段回憶。


怎麼開始(不用一次做完)

第一步不是建系統,是補現有那幾份的身分證。

你平台上大概有三到五份重要的資料。手寫那幾張卡,半天就能寫完。 半天換到「出事時答得出來」,這個交易很划算。

第二步才是把它自動化——在 pipeline 加一棒 ingest, 產出資料的同時產出那張卡。

⚠️ 順序不要反過來。先做自動化,你會花兩週寫一個沒人用的東西; 先手寫幾張,你會知道哪些欄位真的需要。


誠實標一下

我也還沒做。

我的 lab 上那份 99 MB 語料,到今天為止仍然只有檔名、日期、大小。 我知道它是中文維基(因為是我自己下載的), 但那份知識在我腦子裡,不在平台上。

而這正是問題所在:「我知道」和「查得到」是兩件事, 而只有後者能在三個月後、或在我不在的時候,回答問題。


給驗收的一句

「請給我看訓練資料的來源、下載日期、和授權。」

如果對方指著一個 S3 路徑說「就在那裡」,那你們有的是一個檔案, 不是一份可以追溯的訓練資料

而模型的所有問題,最後都會追回到這裡。


你們平台上的訓練資料,查得到「哪來的、什麼時候抓的、誰處理過」嗎?

🧪 這篇的實驗環境與 lab 檔案(最後更新 2026-08-30)

叢集

  • CRC 2.63.0 · OpenShift 4.22.7 · Kubernetes v1.35.6
  • 單節點:13 vCPU / 40 GiB RAM / 120 GB disk
  • 宿主:Framework Laptop 16(Ryzen AI 7 350 · 8C/16T · 64 GB RAM · 1 TB NVMe · RTX 5070 顯卡模組)
  • ⚠️ 叢集內看不到 GPU(CRC 是 VM,RTX 5070 未 passthrough)

Operator

  • opendatahub-operator.v3.5.0 ← 即 RHOAI 3.x 的上游開源版
  • cert-manager-operator.v1.20.0(3.x 的必要相依;2.x 不需要)
  • openshift-pipelines-operator-rh.v1.23.2openshift-gitops-operator.v1.21.3

DataScienceCluster 開啟的元件

  • kserveaipipelinesdashboardworkbenchesmodelregistrykueue(Unmanaged)
  • 其餘(raytrustyaifeastaigateway…)為 Removed

叢集外的依賴(跑在宿主的 podman 上,crc start 不會帶起來)

  • Harbor v2.15.2(私有 registry)· MinIO(S3)

模型端

  • Python 3.12.13 · PyTorch 2.11.0+cu128 · FastAPI + uvicorn · prometheus-client

lab 檔案

⚠️ ODH ≠ RHOAI:元件同源,但 namespace 與部分名稱不同 (我這裡是 opendatahub,商用版是 redhat-ods-* 那一套)。 指令的邏輯可以照用,字串要自己對一次。