Đặt random_state thôi là chưa đủ. Kết quả huấn luyện phụ thuộc bốn nhóm yếu tố, cần cố định cả bốn.
1. Ngẫu nhiên trong thuật toán. Khởi tạo trọng số, xáo trộn dữ liệu, chia fold, dropout, lấy mẫu bootstrap, subsample của boosting. Cố định seed ở mọi thư viện đang dùng (random, numpy, framework học sâu) và truyền random_state tường minh cho từng estimator, thay vì dựa vào seed toàn cục.
2. Dữ liệu. Đây là nguyên nhân bị bỏ sót nhiều nhất. Truy vấn SELECT ... WHERE created_at > ... chạy lại sau một tuần cho tập dữ liệu khác. Cần phiên bản hoá dữ liệu: chốt snapshot bất biến kèm mã băm nội dung, ghi lại đúng truy vấn và mốc thời gian đã dùng, không huấn luyện trực tiếp từ bảng đang thay đổi.
3. Môi trường. Khác phiên bản thư viện là khác thuật toán mặc định (giá trị mặc định của siêu tham số vẫn thay đổi giữa các bản). Khoá phiên bản bằng lockfile hoặc image container. Trên GPU còn có phép cộng dấu phẩy động không kết hợp và các kernel không xác định — cần bật chế độ deterministic nếu bắt buộc khớp từng chữ số, chấp nhận chậm hơn.
4. Song song. Thứ tự gộp kết quả từ nhiều luồng có thể đổi kết quả ở mức sai số làm tròn; với dữ liệu lớn, chênh lệch này đủ để đổi cả điểm cắt của cây.
Cần ghi lại cho mỗi lần huấn luyện: commit code, mã băm/phiên bản dữ liệu, siêu tham số, seed, phiên bản thư viện, metric và tạo phẩm mô hình. Đó là tối thiểu để tái lập và để truy vết khi mô hình trong production có hành vi bất thường.
Cách nhìn thực tế: tái lập từng chữ số không phải lúc nào cũng cần. Điều bắt buộc là kết luận phải ổn định — nếu chạy lại với 5 seed khác nhau mà mô hình A đôi khi thua mô hình B, thì chênh lệch bạn đang báo cáo nằm trong nhiễu, không phải cải tiến. Luôn báo cáo trung bình và độ lệch chuẩn qua nhiều seed.