iT邦幫忙

2026 iThome 鐵人賽

DAY 5
0

1. 前言:用亂數灌出來的資料什麼都分析不出來

Day 04 把 Live Demo 站接上 GA4 與綠界測試金流,軌道 A 的真實事件已經開始流進 BigQuery,但一個剛上線的小店一天只有零星幾筆點擊,撐不起多觸點歸因、顧客分群或素材成效分析,所以雙軌架構的另一條軌道 B 要用合成資料補上歷史規模。

很多教學做合成資料的方式是用亂數產生幾十萬筆點擊與訂單,這樣做有三個問題:

  • 分析不出東西:每個欄位都是獨立亂數,通路之間沒有先後關係、素材之間沒有好壞差異,歸因模型跑完每個通路分數都差不多
  • 分析後無法驗證:就算模型給了一個看起來合理的結論,也沒有辦法知道它是真的找到了規律還是剛好碰上雜訊
  • 對不上真實資料:合成資料的商品 ID、事件名稱與欄位格式如果和 Live Demo 不一樣,之後兩條軌道根本合不起來

這三個問題的解法其實出自同一個核心概念:合成資料不該是隨機產生的亂數,而是一份預先寫好解答的考卷,只要先設定好要植入哪些商業訊號,後續的每一篇分析就能有標準答案可供對照,例如答案表記載 ROAS 異常從 8/12 開始,Day 09 就能驗證 Gemini 有沒有找出這一天,答案表記載有人物的圖片點擊率比較高,Day 17 就能驗證 AI 有沒有看出這個規律,AI 是否分析正確,翻開答案表便一目瞭然。

今日核心目標:

  1. 定義合成器的資料契約:三份設定檔、五張原始表,全部和 Day 04 的商品 ID 與事件欄位對齊
  2. 設計七個植入的商業訊號,讓後面的歸因、診斷、分群與素材分析都有標準答案
  3. 在 Cloud Shell 用內建的 python3 跑出第一週的樣本資料,確認格式與統計分佈

原本預計一天完成的合成器改拆為兩天處理:今天專注於設計好契約與訊號並執行小樣本測試,明天 Day 06 再量產 90 天的資料並匯入 BigQuery,這份契約一旦確立,後續十幾篇內容都會以此為基礎,因此經過思索後,決定必須多花一天時間先行奠定良好的基礎。


2. 系統架構全景與設計理念

Day 05 合成器資料契約:三份設定檔、合成器與五張原始表

💡 核心工程理念:

  1. 契約先行:商品、素材與植入的訊號全部來自三份設定檔,通路基準值等少數常數集中放在程式開頭,改訊號或素材不用動程式碼
  2. 和真實事件同一套 ID:商品、價格、尺寸與活動直接讀 Live Demo 的 products.json,事件欄位沿用 GA4 匯出到 BigQuery 的命名,兩條軌道合併後用 data_source 欄位區分
  3. 答案表就是設定檔:植入的訊號寫在 ground_truth.json,合成器讀它來產生資料,分析篇讀它來對答案,兩邊永遠不會不一致
  4. 可以重現:固定亂數種子,讀者在自己的 Cloud Shell 跑出來的資料和本文一模一樣,而且只用 Python 標準函式庫,不需要安裝任何套件

3. 核心技術深度拆解

3.1 資料契約:三份設定檔與五張原始表

三份設定檔各管一件事:

檔案 負責什麼 誰會用到
live-demo/products.json 5 款商品的 ID、定價、尺寸與 2 檔活動 Live Demo 與合成器共用
synthesizer/creatives.json 30 則廣告素材的通路、活動、廣告群組與設計屬性 合成器、Day 16 視覺分析
synthesizer/ground_truth.json 七個植入訊號的參數 合成器、各分析篇對答案

合成器讀進這三份設定之後產出五張原始表:

原始表 一列代表 90 天筆數
raw_creatives 一則素材 30
raw_ad_daily 一則素材的一天成效(曝光、點擊、花費) 1,700
raw_events 一個網站事件 約 52 萬
raw_orders 一筆訂單 約 3,300
raw_customers 一位顧客(含假個資) 約 2,500

這五張表刻意維持「原始」的樣子,就像從廣告平台、GA4 與金流後台各自匯出來的資料,彼此之間要靠 creative_id、transaction_id、customer_id 串起來,Day 07 的星狀綱要要做的正是把這些散落的原始資料整理成事實表與維度表,完整欄位定義放在儲存庫的 synthesizer/README.md。

3.2 和 Day 04 的真實事件對齊

Day 04 的明日預告寫過合成資料要沿用同一組商品 ID 與事件欄位,具體來說對齊了四件事:

  • 商品與價格:合成器啟動時直接讀 Live Demo 的 products.json,素材裡引用的商品只要不在清單上就直接丟出錯誤並停止,不會悄悄產生對不上的資料
  • 事件名稱:只用 GA4 自動事件(first_visit、session_start)與 Day 04 實際送出的電子商務事件(view_item_list、select_item、view_promotion、select_promotion、view_item、begin_checkout、purchase),不自創事件
  • 欄位命名:event_date、event_timestamp、user_pseudo_id 照 GA4 匯出表的命名與格式,event_date 用台北時間的日期,event_timestamp 用 UTC 微秒
  • 來源標記:付費通路的 utm 三個欄位照 Live Demo 記錄進站來源的格式,自然搜尋與直接進站沿用 GA4 的 (organic)、(direct) 標記,訂單編號用 SY 開頭,和 Live Demo 的 DM 開頭分開

有一點要事先說明,真實的 GA4 匯出表是巢狀結構,ga_session_id 放在 event_params 裡、商品資料放在 items 陣列裡,raw_events 則是把這些欄位攤平後的樣子,一列一個事件、一格一個值,Day 04 提過的 UNNEST 會在 Day 07 用到,把真實事件攤成同樣的形狀再和合成資料合併。

合成資料的期間設在 6/19 到 9/16 共 90 天,之後交給 Live Demo 的真實事件,在 BigQuery 裡把兩邊疊在一起看,就是一條從歷史延伸到今天的完整時間軸。

3.3 讓數字像真的:統計分佈怎麼來

合成資料最常見的破綻是數字太整齊,這次的做法是只設定少數幾個基準值,對齊企劃設定的點擊率 1.5–3.5% 與轉換率 1.8–2.5%,其他數字讓它從模擬過程中自然長出來:

指標 設定方式 90 天結果
點擊率 各通路基準值再乘上素材屬性效果與每日波動 google 搜尋 2.98%、meta 2.03%、line 2.10%
轉換率 各通路基準值乘上依造訪次數遞增的倍數,第一次來的人很少買 以工作階段計 2.29%
客單價 不另外抽分佈,由定價乘上數量自然形成 平均 622 元、中位數 360 元

客單價特別值得一提,常見做法是抽一個常態分佈當訂單金額,但這樣會出現 537.28 元這種不存在的價格甚至抽到負數,這裡的金額一律是「商品定價乘上數量」,所以每一筆都能在 Live Demo 上真的買到,分佈自然是右偏的:最常見的訂單是一雙 180 元的襪子,少數人一次買三、四條浴巾。

造訪行為也照真實網站的樣子設計:午休與晚上是造訪高峰,訪客來過之後十四天內都可能再回來,每個人的造訪一律依時間先後處理,其餘細節放在 README。

3.4 七個植入的訊號

這是整個合成器的核心,每個訊號都對應一種行銷人實際會遇到的狀況,也對應後面某幾篇要驗證的分析方法:

代號 植入的訊號 對應的實務狀況 在哪幾天找回來
S1 meta 重訓襪專案開發新客廣告群組 8/12 起 CPC 翻倍、轉換不變 競價變貴但沒人發現,ROAS 理論上腰斬 Day 09、24、28
S2 8/27 整天沒有 purchase 事件,但訂單照常成立 網站改版弄壞追蹤碼 Day 09、24
S3 某一則 meta 素材點擊率每週衰退約 8% 素材疲乏 Day 09、17
S4 圖片有人物 ×1.25、CTA 在右下 ×1.10、暖色系 ×1.10 素材設計影響點擊 Day 17、18、20
S5 四種顧客:高頻買襪、浴巾大量一次性、組合包新客、沉睡客 顧客價值差異 Day 11、12
S6 meta 多出現在購買路徑開頭、google 搜尋多在最後一步 最後點擊歸因低估社群廣告 Day 08
S7 9/1 起秋日專案期間專案商品占比上升 檔期活動成效 Day 08、09

S2 是 Day 04 前言提過的「事件根本沒送出去」,這個訊號特別放在 GA4 事件表和訂單表之間,只看事件的人會以為那天營收歸零,對照訂單表才會發現是追蹤出問題,這正是 Day 09 要讓 Gemini 學會分辨的狀況。

S6 是 Day 08 歸因篇要驗證的現象,社群廣告負責讓人第一次認識品牌,搜尋廣告負責促成最後一次成交,如果只看最後一次點擊,meta 的功勞幾乎會被抹掉,合成器用「各通路帶來新訪客的比例」與「造訪次數越多越可能成交」兩個設定讓這個現象自然出現,而不是直接指定每筆訂單的歸因。

訊號的強度也刻意調整過,強到用正確的方法一定找得到,但又不至於強到隨便看一張報表就一眼看穿,例如 S4 的人物效果只有 1.25 倍,混在每則素材本身的個體差異與每日波動裡,要把 24 則圖片素材放在一起比較才看得出來,Day 13 會用答案表逐一檢查七個訊號是不是都被前面幾篇找回來了。

3.5 素材設計規格表

S4 要成立有一個前提:每則圖片素材的設計屬性要先決定好,而且各屬性之間不能綁在一起,如果有人物的圖剛好都是暖色系,分析時就分不清點擊率高是因為人物還是因為顏色。

所以 creatives.json 裡的 24 則圖片素材,四個屬性(有無人物、CTA 位置、主色、文字密度)在開發新客與再行銷兩種受眾裡都各自平均分配,彼此之間也盡量不相關,因為再行銷的對象本來就來過網站,點擊率天生比較高,比較素材時要在同一種受眾裡比,每一則還附上生圖用的提示詞,實際的圖片會在 Day 15 之前依這份規格產生,這份規格表同時也是 Day 16 讓 Gemini 抽取視覺特徵時的標準答案,Gemini 說這張圖有人物、CTA 在右下,我們可以直接對照規格表算出它答對幾成,Day 20 的模型評測就會用到這個準確率。


4. FinOps 成本防護實踐:三道防線體系

  1. 第一道防線:善用 Google Cloud 每月免費額度:今天的合成器全部在 Cloud Shell 裡執行,不呼叫任何雲端服務,費用為零,90 天完整資料輸出成 CSV 約 70 MB,明天灌進 BigQuery 後也遠低於每月 10 GiB 的免費儲存額度
  2. 第二道防線:架構層被動成本防護:明天灌資料時使用 BigQuery 的批次載入工作,載入工作本身不收費,不用逐筆寫入的串流方式,合成器也不呼叫 Gemini,今天沒有任何 Token 成本
  3. 第三道防線:Cloud Billing 預算警報:沿用 Day 03 設定的預算警報(新台幣帳戶 NT$ 300/美元帳戶 US$ 10),50%、80%、100% 三段通知,今天的操作不會觸發

5. Cloud Shell 實戰演練:一分鐘跑出第一週資料

5.1 事前準備

  • 已完成 Day 03 的環境建置,並在 Cloud Shell 裡 clone 過本系列的儲存庫
  • Cloud Shell 內建的 python3 即可,不需要安裝任何套件

一樣提供兩條路線,兩條路線的結果完全相同,可以依需求選擇:

5.2 路線 A|懶人包:一行指令跑出一週樣本

cd ~/ai-driven-martech-pipeline && git pull && cd synthesizer && python3 synthetic_pipeline.py --days 7 --out ./sample

5.3 路線 B|逐步教學:理解每一個指令

步驟 1:取得最新程式碼

cd ~/ai-driven-martech-pipeline
git pull
cd synthesizer
ls

會看到 synthetic_pipeline.py、creatives.json、ground_truth.json、validate.py、README.md 與 tests/,合成器會從上一層的 live-demo/products.json 讀商品資料。

步驟 2:跑一週的樣本

python3 synthetic_pipeline.py --days 7 --out ./sample

執行完會印出摘要,第一週只有常態廣告在跑,所以不會有 view_promotion 事件:

{
  "period": "2026-06-19 ~ 2026-06-25",
  "rows": {
    "raw_ad_daily": 70,
    "raw_events": 26458,
    "raw_orders": 138,
    "raw_customers": 138
  },
  "ctr_by_channel": {
    "meta": 0.0217,
    "line": 0.0207,
    "google_cpc": 0.0311
  },
  "sessions": 7345,
  "cvr_session": 0.0188,
  "aov": 708.6,
  "event_names": {
    "first_visit": 3845,
    "session_start": 7345,
    "view_item_list": 7343,
    "select_item": 3753,
    "view_item": 3753,
    "begin_checkout": 281,
    "purchase": 138
  }
}

步驟 3:看看原始資料長什麼樣子

head -3 sample/raw_ad_daily.csv
head -3 sample/raw_orders.csv
tail -n +2 sample/raw_events.csv | cut -d, -f3 | sort | uniq -c | sort -rn

最後一行會列出每種事件的筆數,除了代表新訪客的 first_visit 之外,可以看到從 session_start 到 purchase 大致遞減的轉換漏斗。

步驟 4:確認可以重現

python3 synthetic_pipeline.py --days 7 --out ./sample2
md5sum sample/raw_events.csv sample2/raw_events.csv

兩個檔案的雜湊值會完全相同,換一個 --seed 則會得到另一份資料,但統計特性與植入的訊號都一樣。

步驟 5:跑單元測試

python3 -m unittest discover -s tests -v

測試會用兩個種子各跑一次完整 90 天資料,檢查商品 ID、金額、每位訪客的第一個事件是否為 first_visit,以及七個植入的訊號是不是都找得到,約需 1 分鐘,過程中會印出幾段摘要與幾行 usage 錯誤訊息,那是測試刻意觸發的,最後看到 OK 就是全部通過。

5.4 驗證成果

  • 摘要中三個通路的點擊率都落在 1.5% 到 3.5% 之間
  • raw_orders.csv 的 item_id 只會出現 Live Demo 上的 5 款商品
  • raw_customers.csv 的 email 全部是 example.com 結尾

5.5 不用了?指令全部清除

rm -rf ~/ai-driven-martech-pipeline/synthesizer/sample ~/ai-driven-martech-pipeline/synthesizer/sample2

今天沒有建立任何雲端資源,刪掉樣本資料夾就清乾淨了。


6. 工程實務避坑指南

  1. 客單價不要直接抽常態分佈:抽出來的金額不會是任何商品組合買得到的價格,還可能是負數,應該讓金額由定價乘上數量產生
  2. 回訪要依時間先後處理:一開始我把一天內的造訪打亂順序處理,結果出現「第二次造訪比第一次還早」的訪客,歸因路徑整個錯亂,後來改成先替每次造訪抽好時間再依序處理
  3. 同一人的兩次造訪不能交錯:GA4 同一個瀏覽器要閒置超過 30 分鐘才會開始新的工作階段,多開分頁仍然是同一個工作階段,事件只是在裡面穿插,但合成器是一次造訪一次造訪地產生,可能讓第二次造訪在第一次還沒結束時就開始,出現真實資料裡不存在的兩個重疊工作階段,所以合成器遇到這種情況會把後一次挪到 30 分鐘之後
  4. 日期與時間戳記的時區不同:GA4 匯出表的 event_date 用的是 GA4 資源的報表時區(台北),event_timestamp 則是 UTC 微秒,自己產生資料時兩個欄位要分開處理,不然跨午夜的事件會被算到錯的日期
  5. 假個資也要小心:email 一律用 example.com 這個保留網域,確保就算有人拿去寄信也不會寄到真人信箱
  6. 答案表不要讓分析程式讀:ground_truth.json 和 customer_segments.csv 只拿來對答案,分析篇的查詢與 prompt 都不能引用它們,不然就會變成球員兼裁判

7. 總結與明日預告

今天把合成器的資料契約定下來了:三份設定檔決定商品、素材與訊號,五張原始表模擬廣告平台、GA4 與金流後台各自的匯出資料,七個植入的訊號讓後面每一篇分析都有標準答案可以對,而且和 Day 04 的真實事件用的是同一套商品 ID 與欄位命名。

回頭看前言的三個問題,分析不出東西是因為資料裡沒有規律,所以我們主動植入規律,無法驗證是因為沒有答案,所以答案表和設定檔是同一份,對不上真實資料是因為兩邊各做各的,所以合成器直接讀 Live Demo 的商品檔。

明日預告:Day 06《合成器量產:90 天 50 萬筆灌入 BigQuery 與分佈驗證》,我們將把今天的合成器放大到完整 90 天,用批次載入工作灌進 BigQuery,並逐項驗證統計分佈與七個訊號是否都完整保留下來!


上一篇
Day 04 | 即時驗證軌:極簡 Live Demo 站與電商事件追蹤
下一篇
Day 06 | 合成器量產:90 天 50 萬筆灌入 BigQuery 與分佈驗證
系列文
AI-Driven MarTech:用 Google Cloud + Vertex AI 打造全自動廣告歸因與多模態素材分析系統 共 18 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言