Giới thiệu
Theo báo cáo của Cục Thương mại điện tử và Kinh tế số (Cục TMĐT VN) năm 2024, 73% doanh nghiệp Việt Nam gặp khó khăn trong việc xác định kênh marketing nào thực sự mang lại chuyển đổi. Mô hình last‑click truyền thống đang làm lệch lạc bức tranh phân bổ ngân sách, dẫn đến lãng phí và hiệu quả thấp. Google Tempo 2025 chỉ ra rằng các doanh nghiệp áp dụng data‑driven attribution đạt mức tăng 15% ROAS so với cách làm cũ.
Trong bài viết này, chúng ta sẽ đi sâu vào một phương pháp attribution mạnh mẽ: sử dụng Markov Chains – mô hình stochastic để phân tích chuỗi tương tác (path‑to‑purchase) của khách hàng. Bằng cách coi mỗi kênh là một trạng thái, chúng ta có thể tính toán xác suất chuyển đổi tổng thể và đánh giá đóng góp thực sự của từng kênh thông qua hiệu ứng loại bỏ (removal effect). Bài viết không chỉ trình bày lý thuyết mà còn cung cấp đầy đủ hướng dẫn triển khai thực tế: từ kiến trúc hệ thống, chi phí, timeline, tài liệu bàn giao, cho đến code mẫu chạy được ngay.
🛡️ Lưu ý: Việc thu thập dữ liệu người dùng phải tuân thủ GDPR và Luật bảo vệ dữ liệu cá nhân của Việt Nam. Cần có cơ chế ẩn danh và xin phép khi cần.
Tổng quan về Path‑to‑Purchase và Markov Chains
Path‑to‑Purchase là gì?
Path‑to‑Purchase mô tả hành trình của khách hàng từ lúc nhận biết sản phẩm/dịch vụ cho đến khi thực hiện chuyển đổi (mua hàng, điền form, v.v.). Trong thế giới đa kênh hiện nay, một khách hàng có thể tiếp xúc với nhiều điểm chạm (touchpoint) như:
- Organic Search (tìm kiếm tự nhiên)
- Paid Search (quảng cáo tìm kiếm)
- Social Media (Facebook, Instagram, TikTok)
- Email Marketing
- Direct (truy cập trực tiếp)
- Referral (giới thiệu từ website khác)
- Display Ads (quảng cáo hiển thị)
Các mô hình attribution truyền thống (last‑click, first‑click, linear, time‑decay) đều có những hạn chế vì chúng gán toàn bộ hoặc phần trăm cố định cho một số kênh, không phản ánh đúng tương tác thực tế.
Markov Chains trong Attribution
Markov Chain là một quá trình ngẫu nhiên mà trạng thái tiếp theo chỉ phụ thuộc vào trạng thái hiện tại. Ứng dụng vào attribution, chúng ta định nghĩa:
- Trạng thái: mỗi kênh marketing, cộng thêm hai trạng thái hấp thụ (absorbing states):
conversion(chuyển đổi) vàdropout(rời bỏ). - Xác suất chuyển tiếp: xác suất để người dùng di chuyển từ kênh A sang kênh B, hoặc từ kênh cuối cùng sang
conversion/dropout.
Từ dữ liệu lịch sử, ta đếm số lần chuyển tiếp giữa các trạng thái và xây dựng ma trận chuyển tiếp (P):
[
P = \begin{pmatrix}
p_{11} & p_{12} & \dots & p_{1n} \
p_{21} & p_{22} & \dots & p_{2n} \
\vdots & \vdots & \ddots & \vdots \
p_{n1} & p_{n2} & \dots & p_{nn}
\end{pmatrix}
]
Trong đó (p_{ij}) là xác suất chuyển từ trạng thái (i) sang trạng thái (j).
Chia ma trận thành các phần:
- (Q): ma trận con cho các trạng thái không hấp thụ (transient) → transient.
- (R): ma trận từ transient sang absorbing.
Tính ma trận cơ bản (fundamental matrix):
[
N = (I – Q)^{-1}
]
Xác suất hấp thụ (absorption probabilities):
[
B = N R
]
Xác suất chuyển đổi tổng thể từ trạng thái bắt đầu (start) là phần tử tương ứng trong (B).
Removal Effect và đóng góp của kênh
Để đánh giá tầm quan trọng của một kênh, ta loại bỏ nó khỏi đồ thị (xóa mọi cạnh liên quan) và tính lại xác suất chuyển đổi. Hiệu ứng loại bỏ của kênh (k) là:
[
\text{Removal Effect}k = \frac{\text{Conv}{\text{all}} – \text{Conv}{\text{without }k}}{\text{Conv}{\text{all}}}
]
Sau đó chuẩn hóa các giá trị này để tổng bằng 100% → phần trăm đóng góp của từng kênh. Phương pháp này tương đương với việc tính Shapley value cho các kênh.
⚡ Ưu điểm: Mô hình Markov tự động học từ dữ liệu, không cần giả định trước, phù hợp với hành trình phức tạp nhiều kênh.
Dữ liệu và công cụ cần thiết
Dữ liệu cần thu thập
Để xây dựng mô hình, bạn cần dữ liệu về chuỗi tương tác của người dùng trước khi chuyển đổi (hoặc không). Cụ thể:
- user_id (hoặc client_id) để theo dõi xuyên suốt các phiên.
- session_id để phân nhóm các sự kiện trong một phiên.
- timestamp thời gian xảy ra.
- channel – kênh marketing tương ứng (phân loại dựa trên UTM, nguồn referral, v.v.).
- event_type (ví dụ:
page_view,purchase). - conversion_value (giá trị đơn hàng, nếu có).
Nguồn dữ liệu phổ biến:
- Google Analytics 4 (GA4) với tính năng export sang BigQuery.
- Adobe Analytics, Mixpanel, hoặc tự thiết kế event tracking.
- Dữ liệu từ CRM, email marketing platforms (cần tích hợp qua API).
Công cụ đề xuất
Dưới đây là workflow tổng quan từ thu thập đến báo cáo:
[Khách hàng]
|
v
[Website/App] --(sự kiện)--> [Google Tag Manager]
|
v
[Google Analytics 4] --(export)--> [BigQuery]
|
v
[Airflow DAG] --(trigger)--> [Python Model]
|
v
[BigQuery Results] --(kết nối)--> [Looker Studio]
|
v
[Dashboard cho Marketing]
Các thành phần chính:
- Data Warehouse: BigQuery (hoặc Redshift, Snowflake) – lưu trữ raw events.
- Orchestration: Apache Airflow (Cloud Composer) – điều phối ETL hàng ngày.
- Xử lý mô hình: Python với thư viện pandas, numpy, scipy.
- Visualization: Looker Studio, Tableau, hoặc Metabase.
Xây dựng mô hình Markov Chains từ dữ liệu thực tế
Bước 1: Chuẩn bị dữ liệu – Sessionization
Từ bảng raw events, bạn cần nhóm các sự kiện thành các user journey. Mỗi journey là một chuỗi các kênh theo thứ tự thời gian, kết thúc bằng conversion (nếu có mua) hoặc dropout.
Ví dụ SQL (BigQuery) để tạo bảng journeys:
-- Xem full code ở phần Code mẫu
WITH sessions AS (...),
journeys AS (
SELECT
user_id,
session_id,
ARRAY_AGG(channel ORDER BY ts) as path,
MAX(IF(event_type = 'purchase', 1, 0)) as converted
FROM ...
)
SELECT * FROM journeys;
Bước 2: Đếm các chuyển tiếp
Với mỗi journey, ta đếm:
start→ kênh đầu tiên- kênh i → kênh i+1
- kênh cuối →
conversion(nếu converted) hoặcdropout
Kết quả là một dictionary (from_state, to_state) -> count.
Bước 3: Xây dựng ma trận chuyển tiếp và tính xác suất chuyển đổi tổng thể
Chuyển đổi counts thành ma trận xác suất (P). Sau đó tính (Q, R) và dùng công thức đã nêu để có xác suất chuyển đổi từ start.
Bước 4: Tính Removal Effect
Lần lượt loại bỏ từng kênh (xóa mọi transition có liên quan đến kênh đó), tính lại xác suất chuyển đổi. Phần trăm đóng góp = (original – new) / original, sau đó chuẩn hóa.
Bước 5: Lưu kết quả và cập nhật dashboard
Kết quả đóng góp của các kênh được lưu vào bảng BigQuery, từ đó kết nối với Looker Studio để hiển thị trực quan.
🐛 Lưu ý: Mô hình cần được chạy định kỳ (hàng ngày/tuần) để cập nhật theo dữ liệu mới. Cần xử lý incremental để tiết kiệm chi phí.
Kiến trúc hệ thống và chi phí
So sánh các tech stack
Dưới đây là 4 lựa chọn công nghệ phổ biến để triển khai hệ thống phân tích Markov attribution:
| Tiêu chí | Google Cloud Platform (GCP) | AWS | Azure | Open‑source (self‑hosted) |
|---|---|---|---|---|
| Lưu trữ | BigQuery | Redshift | Azure Synapse | PostgreSQL / MariaDB |
| Xử lý / ETL | Dataflow, Python trên Compute Engine | Glue, EMR | Azure Data Factory, Databricks | Python scripts, Spark tự quản lý |
| Orchestration | Cloud Composer (Airflow) | MWAA (Managed Workflows for Airflow) | ADF hoặc Airflow trên AKS | Airflow tự cài |
| BI / Visualization | Looker Studio (free) hoặc Looker (trả phí) | QuickSight | Power BI | Metabase |
| Chi phí ước tính / tháng | $500 – $1.000 (tùy volume) | $600 – $1.200 | $550 – $1.100 | $300 – $500 (server + nhân lực) |
| Độ phức tạp triển khai | 3 (trung bình) | 4 | 4 | 5 (cao) |
| Khả năng mở rộng | Cao | Cao | Cao | Trung bình |
| Hỗ trợ tại Việt Nam | Có | Có | Có | Community |
| Phù hợp | Doanh nghiệp vừa và lớn | Doanh nghiệp lớn | Doanh nghiệp dùng Microsoft ecosystem | Doanh nghiệp nhỏ, đội kỹ thuật mạnh |
💡 Khuyến nghị: Đối với đa số doanh nghiệp tại Việt Nam, stack GCP + BigQuery + Airflow Composer + Looker Studio là tối ưu về chi phí, dễ tích hợp và scale.
Chi phí triển khai và vận hành 30 tháng
Dưới đây là bảng chi tiết chi phí cho stack GCP (bao gồm hạ tầng, nhân sự vận hành) trong 30 tháng (2,5 năm). Giả định doanh nghiệp có quy mô trung bình, dữ liệu tăng 20% mỗi năm, nhân sự tăng lương 5% mỗi năm.
| Hạng mục | Chi tiết | Năm 1 (tháng) | Năm 1 (cả năm) | Năm 2 (tháng) | Năm 2 (cả năm) | Năm 3 (tháng) | Năm 3 (6 tháng) | Tổng 30 tháng |
|---|---|---|---|---|---|---|---|---|
| Hạ tầng Cloud | ||||||||
| BigQuery (storage + query) | $90 | $1,080 | $108 | $1,296 | $130 | $780 | $3,156 | |
| Cloud Composer (Airflow) | $300 | $3,600 | $300 | $3,600 | $300 | $1,800 | $9,000 | |
| Compute Engine (VM nhỏ) | $30 | $360 | $30 | $360 | $30 | $180 | $900 | |
| Network egress | $10 | $120 | $10 | $120 | $10 | $60 | $300 | |
| Phần mềm & Licenses | Looker Studio (free) | $0 | $0 | $0 | $0 | $0 | $0 | $0 |
| Nhân sự vận hành | ||||||||
| Data Engineer (0.5 FTE) | $2,000 | $24,000 | $2,100 | $25,200 | $2,205 | $13,230 | $62,430 | |
| Data Scientist (0.25 FTE) | $1,500 | $18,000 | $1,575 | $18,900 | $1,654 | $9,924 | $46,824 | |
| DevOps (0.2 FTE) | $1,000 | $12,000 | $1,050 | $12,600 | $1,103 | $6,618 | $31,218 | |
| Training & Support | Khóa huấn luyện, hỗ trợ | $42 | $500 | $17 | $200 | $33 | $200 | $900 |
| Tổng cộng hàng tháng | $4,972 | $5,190 | $5,465 | |||||
| Tổng cộng theo năm | $59,660 | $62,276 | $32,792 | $154,728 |
Lưu ý: Chi phí nhân sự có thể thay đổi tùy theo công ty và vị trí địa lý. Bảng trên dựa trên mức lương trung bình tại TP.HCM cho vị trí tương ứng.
Kế hoạch triển khai chi tiết
Dự án được chia thành 8 phase lớn, tổng thời lượng 17 tuần. Mỗi phase có mục tiêu, danh sách công việc con, người phụ trách và phụ thuộc rõ ràng.
Phase 1: Phân tích yêu cầu và thu thập dữ liệu (2 tuần)
Mục tiêu: Xác định business goals, KPI, các kênh cần theo dõi, đánh giá dữ liệu hiện có.
Công việc:
1. Workshop với marketing team để hiểu hành trình khách hàng và các kênh hiện tại.
2. Đánh giá dữ liệu hiện có từ GA4, CRM, quảng cáo.
3. Xác định các metrics cần đo: conversion rate, giá trị đơn hàng, v.v.
4. Thiết kế schema dữ liệu cho events.
5. Lập kế hoạch thu thập dữ liệu bổ sung nếu cần.
6. Tài liệu hóa yêu cầu (Requirements Specification).
Người phụ trách: Product Owner, Data Analyst
Thời gian: Tuần 1 – Tuần 2
Phụ thuộc: Không
Phase 2: Thiết kế kiến trúc và lựa chọn công nghệ (2 tuần)
Mục tiêu: Chọn tech stack, thiết kế pipeline, ước lượng chi phí.
Công việc:
1. So sánh các tech stack (dựa trên bảng so sánh).
2. Lựa chọn stack phù hợp với ngân sách và kỹ năng team.
3. Thiết kế chi tiết kiến trúc hệ thống (data flow, components).
4. Xác định các dịch vụ cloud cần dùng, cấu hình.
5. Lập kế hoạch bảo mật và tuân thủ (GDPR, PDPA).
6. Tài liệu thiết kế kiến trúc (Architecture Design Document).
Người phụ trách: Solution Architect, DevOps
Thời gian: Tuần 3 – Tuần 4
Phụ thuộc: Phase 1 hoàn thành
Phase 3: Xây dựng pipeline thu thập sự kiện (3 tuần)
Mục tiêu: Triển khai tracking code và data ingestion vào data warehouse.
Công việc:
1. Cấu hình Google Tag Manager / GA4 để gửi events đến BigQuery (hoặc tương đương).
2. Thiết lập stream dữ liệu từ các nguồn khác (CRM, email marketing) qua API.
3. Tạo các bảng raw events trong BigQuery.
4. Viết script đảm bảo data quality (validation).
5. Thiết lập Airflow DAG để đồng bộ dữ liệu hàng ngày.
6. Kiểm thử end‑to‑end với dữ liệu mẫu.
Người phụ trách: Data Engineer, Frontend Dev (cho tracking code)
Thời gian: Tuần 5 – Tuần 7
Phụ thuộc: Phase 2 hoàn thành
Phase 4: Xử lý dữ liệu và xây dựng mô hình (4 tuần)
Mục tiêu: Xây dựng ETL để tổng hợp journeys và tính toán Markov model.
Công việc:
1. Viết SQL query để nhóm events thành user journeys, tạo chuỗi touchpoints.
2. Xây dựng Python script để tính transition counts và ma trận xác suất.
3. Triển khai removal effect và tính toán đóng góp kênh.
4. Tối ưu hiệu năng (caching, incremental processing).
5. Tạo bảng kết quả (channel contributions) trong data warehouse.
6. Viết unit test cho model.
7. So sánh kết quả với mô hình heuristic để validate.
8. Tài liệu hóa logic và công thức.
Người phụ trách: Data Scientist, Data Engineer
Thời gian: Tuần 8 – Tuần 11
Phụ thuộc: Phase 3 hoàn thành (dữ liệu sẵn)
Phase 5: Tích hợp báo cáo và dashboard (2 tuần)
Mục tiêu: Tạo dashboard hiển thị kết quả attribution cho stakeholders.
Công việc:
1. Thiết kế dashboard (Looker Studio, Tableau) với các biểu đồ: contribution per channel, conversion paths, trends.
2. Kết nối với bảng kết quả.
3. Tạo cảnh báo nếu có thay đổi lớn.
4. Viết tài liệu hướng dẫn sử dụng.
5. Training cho marketing team.
6. Nhận phản hồi và điều chỉnh.
Người phụ trách: BI Analyst, Data Engineer
Thời gian: Tuần 12 – Tuần 13
Phụ thuộc: Phase 4 hoàn thành
Phase 6: Kiểm thử và tối ưu (2 tuần)
Mục tiêu: Kiểm thử toàn hệ thống, tối ưu hiệu năng và độ chính xác.
Công việc:
1. Kiểm thử tích hợp toàn bộ pipeline.
2. Kiểm tra data quality: completeness, accuracy, consistency.
3. Tối ưu query và script để giảm chi phí và thời gian xử lý.
4. Thực hiện load testing nếu cần.
5. Đánh giá mô hình với dữ liệu mới, điều chỉnh nếu cần.
6. Cập nhật tài liệu.
Người phụ trách: QA Engineer, Data Engineer
Thời gian: Tuần 14 – Tuần 15
Phụ thuộc: Phase 5 hoàn thành
Phase 7: Triển khai production và giám sát (1 tuần)
Mục tiêu: Đưa hệ thống vào vận hành chính thức, thiết lập monitoring.
Công việc:
1. Deploy code lên môi trường production (Airflow, Cloud Functions, etc.).
2. Cấu hình monitoring (logs, alerts) với Stackdriver, Cloud Monitoring.
3. Thiết lập SLA và thông báo sự cố.
4. Kiểm tra backup và recovery.
5. Thực hiện dry‑run với dữ liệu thật.
6. Kích hoạt schedule cho pipeline.
Người phụ trách: DevOps, Data Engineer
Thời gian: Tuần 16
Phụ thuộc: Phase 6 hoàn thành
Phase 8: Bàn giao và đào tạo (1 tuần)
Mục tiêu: Bàn giao hệ thống và tài liệu cho đội vận hành.
Công việc:
1. Hoàn thiện tất cả tài liệu (theo danh sách bàn giao).
2. Tổ chức buổi training cho end‑users (marketing, analysts).
3. Bàn giao source code, credentials, và quyền truy cập.
4. Ký kết biên bản nghiệm thu.
5. Lập kế hoạch hỗ trợ sau triển khai.
Người phụ trách: Project Manager, Solution Architect
Thời gian: Tuần 17
Phụ thuộc: Phase 7 hoàn thành
Timeline tổng quan (Gantt chart)
Tuần 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Phase 1 [========]
Phase 2 [========]
Phase 3 [===========]
Phase 4 [================]
Phase 5 [======]
Phase 6 [======]
Phase 7 [===]
Phase 8 [=]
Mũi tên thể hiện dependency: Phase sau bắt đầu ngay sau khi Phase trước kết thúc.
Tài liệu bàn giao cuối dự án
Dự án phải bàn giao 15 tài liệu chính sau:
| STT | Tên tài liệu | Người viết (vai trò) | Mô tả nội dung |
|---|---|---|---|
| 1 | Project Charter | Project Manager | Phạm vi, mục tiêu, stakeholders, ràng buộc, rủi ro ban đầu. |
| 2 | Requirement Specification | Product Owner, BA | Yêu cầu chi tiết về business, functional, non‑functional. |
| 3 | Data Dictionary | Data Engineer | Mô tả tất cả bảng, trường, ý nghĩa, nguồn dữ liệu. |
| 4 | Architecture Design Document | Solution Architect | Kiến trúc hệ thống, sơ đồ luồng dữ liệu, công nghệ sử dụng. |
| 5 | API Documentation | Backend Developer (nếu có) | Tài liệu API cho các endpoint tích hợp (nếu có). |
| 6 | Source Code Repository | Development Team | Code đầy đủ với README, hướng dẫn build. |
| 7 | Deployment Guide | DevOps | Các bước triển khai lên môi trường production, cấu hình. |
| 8 | User Manual | BI Analyst | Hướng dẫn sử dụng dashboard, ý nghĩa các chỉ số. |
| 9 | Test Plan | QA Engineer | Kế hoạch kiểm thử: phạm vi, phương pháp, môi trường. |
| 10 | Test Cases | QA Engineer | Các test case chi tiết cho từng thành phần. |
| 11 | Test Report | QA Engineer | Kết quả kiểm thử, đánh giá chất lượng. |
| 12 | Maintenance Plan | DevOps, Data Engineer | Quy trình bảo trì, nâng cấp, xử lý sự cố. |
| 13 | Training Materials | Trainer / PM | Slide, video đào tạo người dùng cuối. |
| 14 | Handover Document | Project Manager | Tóm tắt dự án, trạng thái bàn giao, danh sách công việc còn tồn đọng. |
| 15 | Final Project Report | Project Manager | Báo cáo tổng kết dự án: thành tựu, bài học, khuyến nghị. |
Quản lý rủi ro và phương án dự phòng
| Rủi ro | Mức độ | Phương án B (ngay lập tức) | Phương án C (dài hạn) |
|---|---|---|---|
| Dữ liệu không đầy đủ, thiếu kênh | Trung bình | Sử dụng dữ liệu mẫu từ các nguồn khác, bổ sung tracking code. | Đầu tư vào hệ thống CDP (Customer Data Platform) để tổng hợp dữ liệu. |
| Mô hình cho kết quả không ổn định | Cao | Fallback về mô hình heuristic (time‑decay) trong thời gian hiệu chỉnh. | Thuê chuyên gia data science tinh chỉnh mô hình, thử nghiệm Deep Learning. |
| Chi phí cloud vượt dự toán | Trung bình | Áp dụng query optimization, chuyển sang reserved instances, giảm tần suất chạy. | Chuyển sang kiến trúc hybrid (phần lưu trữ rẻ hơn như AWS S3 + Athena). |
| Khó khăn tích hợp với nguồn dữ liệu | Cao | Sử dụng công cụ trung gian như Segment hoặc Fivetran để đơn giản hóa integration. | Xây dựng custom connector dựa trên API của từng nguồn. |
| Thay đổi yêu cầu từ business | Trung bình | Ưu tiên các tính năng core, lập change request và điều chỉnh timeline. | Áp dụng Agile, chia nhỏ thành các sprint để dễ thích nghi. |
| Vấn đề bảo mật dữ liệu cá nhân | Cao | Mã hóa dữ liệu, ẩn danh hóa, tuân thủ ngay từ đầu. | Thực hiện audit định kỳ, đào tạo nhân sự về GDPR/PDPA. |
KPI và đo lường hiệu quả
| KPI | Công cụ đo | Tần suất đo | Mục tiêu |
|---|---|---|---|
| Độ chính xác của attribution | So sánh với A/B test | Hàng tháng | Sai số < 5% so với test thực tế |
| Thời gian xử lý dữ liệu (data freshness) | Airflow logs, Monitoring | Hàng ngày | Dữ liệu cập nhật trong vòng 2h |
| Tỷ lệ sử dụng dashboard | Google Analytics cho BI | Hàng tuần | > 70% người dùng target truy cập |
| Cải thiện ROAS | Báo cáo tài chính | Hàng quý | Tăng ít nhất 10% so với baseline |
| Chi phí vận hành hàng tháng | Cloud Billing, nhân sự | Hàng tháng | Nằm trong ngân sách (≤ $5,000) |
| Số lỗi trong pipeline | Airflow alerts, Logging | Hàng ngày | 0 lỗi nghiêm trọng |
Checklist Go‑live
Trước khi chính thức đưa hệ thống vào vận hành, cần kiểm tra kỹ các hạng mục sau. Checklist được chia thành 5 nhóm, tổng cộng 45 mục.
Security & Compliance
- [ ] Mã hóa dữ liệu ở trạng thái nghỉ (at‑rest encryption) đã bật trên BigQuery và Cloud Storage.
- [ ] Mã hóa dữ liệu trên đường truyền (TLS 1.2+).
- [ ] Access control (IAM) được cấu hình theo nguyên tắc least privilege.
- [ ] Đã loại bỏ các secret khỏi code, sử dụng Secret Manager.
- [ ] Có cơ chế ẩn danh hóa PII (nếu cần).
- [ ] Đã thực hiện đánh giá tác động bảo mật (DPIA) nếu xử lý dữ liệu nhạy cảm.
- [ ] Tuân thủ GDPR/PDPA: có Privacy Policy, cơ chế consent.
- [ ] Đã cấu hình VPC Service Controls (nếu cần).
- [ ] Audit logging được bật và lưu trữ ít nhất 90 ngày.
Performance & Scalability
- [ ] Query BigQuery đã được tối ưu (partitioning, clustering).
- [ ] Airflow DAG chạy trong thời gian cho phép (< 30 phút).
- [ ] Có cơ chế auto‑scaling cho Compute Engine (nếu dùng).
- [ ] Đã thực hiện load test với lượng dữ liệu gấp 3 lần dự kiến.
- [ ] Sử dụng incremental processing để tránh quét toàn bộ bảng.
- [ ] Đã thiết lập caching cho dashboard (nếu cần).
- [ ] Có kế hoạch mở rộng khi dữ liệu tăng (sharding, BigQuery reservation).
- [ ] Đã đo latency từ khi dữ liệu đến đến khi có báo cáo (< 2h).
- [ ] Đã tối ưu Python script (sử dụng vectorization, tránh vòng lặp).
Business & Data Accuracy
- [ ] Dữ liệu raw events đã được validate (không null channel, định dạng timestamp).
- [ ] Số lượng journeys tạo ra khớp với số session trong GA4 (sai số < 2%).
- [ ] Kết quả attribution đã được so sánh với mô hình last‑click để phát hiện bất thường.
- [ ] Đã kiểm tra cross‑device tracking (nếu có user‑id).
- [ ] Có cơ chế xử lý duplicate events (deduplication).
- [ ] Đã thực hiện reconciliation giữa tổng conversion từ model và hệ thống order.
- [ ] Dashboard hiển thị đúng dữ liệu mới nhất.
- [ ] Có unit test cho các hàm quan trọng (transition counting, matrix inversion).
- [ ] Đã chạy mô hình trên dữ liệu lịch sử và so sánh với kết quả đã biết.
Payment & Finance
- [ ] Đã thiết lập budget alert trên GCP (80%, 100%).
- [ ] Các tài nguyên cloud được gán label để theo dõi chi phí theo dự án.
- [ ] Có quy trình đối soát chi phí hàng tháng giữa kế toán và team kỹ thuật.
- [ ] Đã tính toán ROI dự kiến sau triển khai.
- [ ] Có kế hoạch dự phòng ngân sách cho năm tiếp theo.
- [ ] Đã xác định các mục có thể cắt giảm chi phí nếu cần (ví dụ: downgrade VM).
- [ ] Đã tích hợp với hệ thống invoice (nếu cần).
- [ ] Đã đánh giá chi phí nhân sự vận hành và đưa vào ngân sách.
Monitoring & Rollback
- [ ] Đã cấu hình Cloud Monitoring cho BigQuery slot usage, query times.
- [ ] Airflow có cảnh báo qua email/Slack khi DAG fail.
- [ ] Có dashboard giám sát tổng thể (health check).
- [ ] Đã viết runbook xử lý sự cố thường gặp.
- [ ] Có cơ chế rollback về phiên bản code trước (Git tags, Docker image versioning).
- [ ] Đã test rollback: khôi phục bảng dữ liệu từ snapshot.
- [ ] Có lịch trình backup định kỳ cho cơ sở dữ liệu (nếu dùng PostgreSQL).
- [ ] Đã thiết lập SLI/SLO cho hệ thống (ví dụ: availability 99.9%).
- [ ] Đã phân công người on‑call ứng phó sự cố.
Code và Config mẫu
Dưới đây là 12 đoạn code/config thực tế bạn có thể sử dụng ngay trong dự án.
1. GA4 Tracking Code với channel tự động
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXX');
// Hàm phân loại channel từ URL
function getChannel() {
const urlParams = new URLSearchParams(window.location.search);
const utmSource = urlParams.get('utm_source');
const utmMedium = urlParams.get('utm_medium');
if (utmSource && utmMedium) return `${utmSource}_${utmMedium}`;
if (document.referrer.includes('google')) return 'organic_search';
if (document.referrer.includes('facebook')) return 'social_facebook';
// ... thêm logic khác
return 'direct';
}
// Gửi page_view với channel
gtag('event', 'page_view', {
'channel': getChannel()
});
</script>
2. BigQuery Schema cho raw_events
CREATE TABLE `project.dataset.raw_events` (
event_id STRING,
user_id STRING,
session_id STRING,
timestamp TIMESTAMP,
channel STRING,
event_type STRING,
conversion_value FLOAT64,
page_url STRING,
referrer STRING,
utm_source STRING,
utm_medium STRING,
utm_campaign STRING
)
PARTITION BY DATE(timestamp)
CLUSTER BY channel, event_type;
3. SQL Tổng hợp user journeys
WITH events_with_session AS (
SELECT
user_id,
TIMESTAMP_MICROS(event_timestamp) as ts,
channel,
event_type,
LAG(TIMESTAMP_MICROS(event_timestamp)) OVER (PARTITION BY user_id ORDER BY event_timestamp) as prev_ts,
TIMESTAMP_DIFF(TIMESTAMP_MICROS(event_timestamp), LAG(TIMESTAMP_MICROS(event_timestamp)) OVER (PARTITION BY user_id ORDER BY event_timestamp), MINUTE) as mins_since_prev
FROM `project.dataset.raw_events`
WHERE channel IS NOT NULL
),
session_groups AS (
SELECT
*,
SUM(IF(mins_since_prev IS NULL OR mins_since_prev > 30, 1, 0)) OVER (PARTITION BY user_id ORDER BY ts) as session_number
FROM events_with_session
),
user_sessions AS (
SELECT
user_id,
CONCAT(user_id, '_', session_number) as session_id,
ts,
channel,
event_type
FROM session_groups
),
journeys AS (
SELECT
user_id,
session_id,
ARRAY_AGG(channel ORDER BY ts) as path,
MAX(IF(event_type = 'purchase', 1, 0)) as converted
FROM user_sessions
GROUP BY user_id, session_id
)
SELECT * FROM journeys
4. Python: Đếm transitions từ journeys
from collections import defaultdict
def count_transitions(journeys):
"""
journeys: list of tuples (path_list, converted)
Returns: dict (from_state, to_state) -> count
"""
trans = defaultdict(int)
for path, converted in journeys:
if not path:
continue
# start -> first
trans[('start', path[0])] += 1
# intermediate
for i in range(len(path)-1):
trans[(path[i], path[i+1])] += 1
# last -> conversion or dropout
last = path[-1]
if converted:
trans[(last, 'conversion')] += 1
else:
trans[(last, 'dropout')] += 1
return trans
5. Python: Xây dựng ma trận và tính Removal Effect
import numpy as np
from collections import OrderedDict
def build_states(trans_counts):
states_set = set()
for fr, to in trans_counts:
states_set.add(fr)
states_set.add(to)
states_set.update(['start', 'conversion', 'dropout'])
channels = sorted([s for s in states_set if s not in ('start','conversion','dropout')])
states = ['start'] + channels + ['conversion','dropout']
idx = {s:i for i,s in enumerate(states)}
n = len(states)
P = np.zeros((n,n))
for (fr, to), cnt in trans_counts.items():
i = idx[fr]
j = idx[to]
P[i,j] = cnt
row_sums = P.sum(axis=1, keepdims=True)
P = np.divide(P, row_sums, out=np.zeros_like(P), where=row_sums!=0)
return states, idx, P
def conversion_prob(P, idx):
trans_idx = [i for i,s in enumerate(states) if s not in ('conversion','dropout')]
abs_idx = [idx['conversion'], idx['dropout']]
Q = P[np.ix_(trans_idx, trans_idx)]
R = P[np.ix_(trans_idx, abs_idx)]
I = np.eye(len(trans_idx))
N = np.linalg.inv(I - Q)
B = N @ R
start_pos = trans_idx.index(idx['start'])
conv_pos = list(abs_idx).index(idx['conversion'])
return B[start_pos, conv_pos]
def removal_effect(trans_counts):
states, idx, P = build_states(trans_counts)
overall = conversion_prob(P, idx)
channels = [s for s in states if s not in ('start','conversion','dropout')]
contributions = {}
for ch in channels:
# Loại bỏ mọi transition liên quan đến kênh này
new_counts = {k:v for k,v in trans_counts.items() if ch not in k}
try:
new_states, new_idx, new_P = build_states(new_counts)
new_conv = conversion_prob(new_P, new_idx)
except np.linalg.LinAlgError:
new_conv = 0.0
contributions[ch] = (overall - new_conv) / overall
# Chuẩn hóa
total = sum(contributions.values())
if total > 0:
contributions = {k: v/total for k,v in contributions.items()}
return contributions
6. Dockerfile cho môi trường Python
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "run_model.py"]
7. docker-compose.yml cho Airflow local
version: '3'
services:
postgres:
image: postgres:13
environment:
POSTGRES_USER: airflow
POSTGRES_PASSWORD: airflow
POSTGRES_DB: airflow
volumes:
- postgres-db-volume:/var/lib/postgresql/data
redis:
image: redis:6.2
airflow-webserver:
image: apache/airflow:2.5.1
command: webserver
depends_on:
- postgres
- redis
environment:
AIRFLOW__CORE__EXECUTOR: CeleryExecutor
AIRFLOW__DATABASE__SQL_ALCHEMY_CONN: postgresql+psycopg2://airflow:airflow@postgres/airflow
AIRFLOW__CELERY__RESULT_BACKEND: db+postgresql://airflow:airflow@postgres/airflow
AIRFLOW__CELERY__BROKER_URL: redis://redis:6379/0
volumes:
- ./dags:/opt/airflow/dags
- ./logs:/opt/airflow/logs
- ./plugins:/opt/airflow/plugins
ports:
- "8080:8080"
airflow-scheduler:
image: apache/airflow:2.5.1
command: scheduler
depends_on:
- postgres
- redis
environment:
AIRFLOW__CORE__EXECUTOR: CeleryExecutor
AIRFLOW__DATABASE__SQL_ALCHEMY_CONN: postgresql+psycopg2://airflow:airflow@postgres/airflow
AIRFLOW__CELERY__RESULT_BACKEND: db+postgresql://airflow:airflow@postgres/airflow
AIRFLOW__CELERY__BROKER_URL: redis://redis:6379/0
volumes:
- ./dags:/opt/airflow/dags
- ./logs:/opt/airflow/logs
- ./plugins:/opt/airflow/plugins
airflow-worker:
image: apache/airflow:2.5.1
command: celery worker
depends_on:
- postgres
- redis
environment:
AIRFLOW__CORE__EXECUTOR: CeleryExecutor
AIRFLOW__DATABASE__SQL_ALCHEMY_CONN: postgresql+psycopg2://airflow:airflow@postgres/airflow
AIRFLOW__CELERY__RESULT_BACKEND: db+postgresql://airflow:airflow@postgres/airflow
AIRFLOW__CELERY__BROKER_URL: redis://redis:6379/0
volumes:
- ./dags:/opt/airflow/dags
- ./logs:/opt/airflow/logs
- ./plugins:/opt/airflow/plugins
volumes:
postgres-db-volume:
8. Airflow DAG chạy model hàng ngày
from airflow import DAG
from airflow.operators.python import PythonOperator
from airflow.providers.google.cloud.operators.bigquery import BigQueryExecuteQueryOperator
from datetime import datetime, timedelta
default_args = {
'owner': 'airflow',
'depends_on_past': False,
'start_date': datetime(2025, 1, 1),
'email_on_failure': True,
'retries': 1,
'retry_delay': timedelta(minutes=5),
}
dag = DAG(
'markov_attribution',
default_args=default_args,
schedule_interval='0 2 * * *',
catchup=False,
)
def run_model(**context):
from model import compute_contributions
compute_contributions()
return 'Done'
extract_journeys = BigQueryExecuteQueryOperator(
task_id='extract_journeys',
sql='''
CREATE OR REPLACE TABLE `project.dataset.journeys` AS
-- SQL từ snippet 3
''',
use_legacy_sql=False,
gcp_conn_id='google_cloud_default',
dag=dag,
)
compute = PythonOperator(
task_id='compute_contributions',
python_callable=run_model,
dag=dag,
)
extract_journeys >> compute
9. Nginx config cho API (nếu cần)
server {
listen 80;
server_name api.yourdomain.com;
location / {
proxy_pass http://localhost:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# SSL config (recommended)
# listen 443 ssl;
# ssl_certificate /path/to/cert;
# ssl_certificate_key /path/to/key;
}
10. Cloudflare Worker thu thập event (edge tracking)
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const url = new URL(request.url)
if (url.pathname === '/track') {
const params = url.searchParams
const eventName = params.get('event') || 'page_view'
const channel = params.get('channel')
const cid = params.get('cid') || generateUUID()
const payload = {
client_id: cid,
events: [{
name: eventName,
params: {
channel: channel,
page_location: params.get('url'),
page_referrer: params.get('ref'),
}
}]
}
// Gửi đến GA4 Measurement Protocol
await fetch('https://www.google-analytics.com/mp/collect?measurement_id=G-XXXX&api_secret=YYYY', {
method: 'POST',
body: JSON.stringify(payload)
})
// Có thể gửi đồng thời đến endpoint riêng
return new Response('OK', { status: 200 })
}
return new Response('Not found', { status: 404 })
}
function generateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8)
return v.toString(16)
})
}
11. GitHub Actions CI/CD
name: CI/CD
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
release:
types: [created]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install dependencies
run: pip install -r requirements.txt
- name: Run unit tests
run: pytest tests/
deploy:
needs: test
if: github.event_name == 'release' && github.event.action == 'created'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Deploy to Cloud Run
uses: google-github-actions/deploy-cloudrun@v0
with:
service: markov-attribution
image: gcr.io/your-project/markov-attribution:${{ github.sha }}
credentials: ${{ secrets.GCP_SA_KEY }}
12. Script validation dữ liệu
from google.cloud import bigquery
client = bigquery.Client()
def validate():
# Số lượng raw events
raw_q = client.query('SELECT COUNT(*) as cnt FROM `project.dataset.raw_events`')
raw_cnt = list(raw_q.result())[0].cnt
print(f"Raw events: {raw_cnt}")
# Số lượng journeys
journeys_q = client.query('SELECT COUNT(*) as cnt FROM `project.dataset.journeys`')
journeys_cnt = list(journeys_q.result())[0].cnt
print(f"Journeys: {journeys_cnt}")
# Kiểm tra null channel
null_channel_q = client.query('''
SELECT COUNT(*) as cnt
FROM `project.dataset.raw_events`
WHERE channel IS NULL
''')
null_channel = list(null_channel_q.result())[0].cnt
if null_channel > 0:
print(f"WARNING: {null_channel} rows have null channel")
# Kiểm tra conversion count khớp với orders
orders_q = client.query('SELECT COUNT(*) as cnt FROM `project.dataset.orders`')
orders_cnt = list(orders_q.result())[0].cnt
conv_q = client.query('SELECT COUNT(*) as cnt FROM `project.dataset.journeys` WHERE converted = 1')
conv_cnt = list(conv_q.result())[0].cnt
if abs(orders_cnt - conv_cnt) > 10:
print(f"DISCREPANCY: orders={orders_cnt}, journeys converted={conv_cnt}")
Kết luận và thảo luận
Phân tích path‑to‑purchase bằng Markov Chains cung cấp một cách tiếp cận khách quan, dựa trên dữ liệu để đánh giá đóng góp thực sự của các kênh marketing. Triển khai mô hình này không quá phức tạp nếu bạn có pipeline dữ liệu vững chắc và tuân theo các bước đã nêu.
Key takeaways:
- Mô hình Markov giúp loại bỏ những giả định chủ quan, tự động học từ hành vi người dùng.
- Cần đầu tư vào việc thu thập dữ liệu đầy đủ, nhất là xuyên suốt các phiên.
- Kiến trúc GCP + BigQuery + Airflow + Python là lựa chọn cân bằng giữa chi phí và hiệu năng.
- Checklist go‑live chi tiết giúp đảm bảo hệ thống sẵn sàng cho production.
❓ Câu hỏi thảo luận: Anh em đã từng triển khai attribution model nào chưa? Có gặp khó khăn gì trong việc xác định đóng góp của các kênh? Hãy chia sẻ ở phần bình luận!
Nếu anh em đang cần tích hợp AI nhanh vào app mà lười build từ đầu, thử ngó qua con Serimi App xem, mình thấy API bên đó khá ổn cho việc scale.
Nội dung được Hải định hướng, trợ lý AI giúp mình viết chi tiết.








