← quay lại đọcBản in thử6 × 9 in · khổ · trang đối diện28 tr · 6,075 chữ
ii
Maxubrq · GT - Chương V
Đường đi ngắn nhất, nhưng "ngắn" theo nghĩa nào
“Thuật toán tìm đường ngắn nhất luôn đúng, thứ sai là định nghĩa của chữ ngắn.”
iii
Maxubrq · GT - Chương V

Đường đi ngắn nhất, nhưng "ngắn" theo nghĩa nào

khi định nghĩa weight mới là bài toán
ETA báo 25 phút, tài xế đi mất 50, và Dijkstra hoàn toàn vô tội. Trong sách, weight là số cho sẵn; trong production, định nghĩa weight chính là bài toán. Kỹ năng cốt lõi là ba quyết định trước khi đụng thuật toán: "tốt nhất" theo nghĩa nào, đồ thị đường chứa luật gì, và trả chi phí tính toán ở đâu.
bởi maxubrq
tháng 6 năm 2026
Đường đi ngắn nhất, nhưng "ngắn" theo nghĩa nào

Dashboard của một app giao đồ ăn, một chiều thứ sáu. Đường cong tỷ lệ huỷ đơn đi lên từ ba giờ chiều, đến năm giờ thì không còn gọi là đường cong nữa, nó là một bức tường. Đội vận hành lọc các đơn bị huỷ, đọc lý do khách điền, và lý do lặp lại nhiều đến mức gần như là một câu trả lời duy nhất: app báo hai mươi lăm phút, tài xế đi mất năm mươi. Khách không huỷ vì đói. Khách huỷ vì bị thất hứa.

Cuộc họp sau đó diễn ra theo kịch bản mà có lẽ bạn đoán được. Ai đó chiếu con số lên màn hình, ai đó hỏi hệ thống tính ETA thế nào, và một câu quen thuộc được nói ra, chắc thuật toán tìm đường có bug.

Đội kỹ thuật làm điều đúng đắn: lấy một đơn cụ thể, log lại đồ thị đường tại thời điểm đó, log lại các con số gắn trên từng đoạn đường, chạy lại phép tính từng bước một. Thuật toán trả về đúng đường đi có tổng nhỏ nhất trên đồ thị nó được đưa, theo đúng các con số nó được giao, chạy lại mười lần ra mười kết quả giống nhau. Dijkstra vô tội.

Và sự vô tội đó mới là tin xấu. Bug thì sửa được, có stack trace, có dòng code để trỏ vào. Còn một hệ thống mà mọi thành phần đều chạy đúng nhưng kết quả vẫn sai gấp đôi, cái sai đó không có địa chỉ, nó phải được đi tìm.

Đi tìm thì cần bản đồ. Một câu trả lời routing đứng trên ba tầng. Tầng dưới cùng là đồ thị, đường nào tồn tại, nối với nhau ở đâu, đi được theo hướng nào. Tầng giữa là weight, mỗi đoạn đường tốn bao nhiêu, tốn theo đơn vị gì. Tầng trên cùng là thuật toán, tìm tổng nhỏ nhất trên những gì hai tầng dưới khai báo. Sách giáo khoa cho sẵn hai tầng dưới, đề bài viết gọn trong một dòng, cho đồ thị G với hàm trọng số w, rồi dành toàn bộ chương để dạy tầng trên cùng. Production thì ngược lại. Tầng trên cùng gần như không bao giờ hỏng, nó là vài trăm dòng code đã được kiểm chứng bốn mươi năm. Hai tầng dưới gần như không bao giờ được cho sẵn.

Tương tác — thử trên web
Diagram

Quay lại đơn hàng bị huỷ. Weight trong hệ thống đó là độ dài đoạn đường chia cho tốc độ tối đa cho phép. Nghĩa là chữ ngắn nhất đang được định nghĩa là nhanh nhất trên một thành phố không có ai khác sống trong đó, không có đèn đỏ, không có giờ tan tầm, không có cơn mưa nào. Hai mươi lăm phút là đáp số đúng của một bài toán không tồn tại.

Ở chương trước, câu hỏi dừng lại ở chỗ đến được hay không, và một danh sách những gì đến được là câu trả lời trọn vẹn. Câu hỏi của chương này bắt đầu ở ngay sau đó, đến bằng đường nào, và chữ nào trong cụm đường tốt nhất mới là chữ khó. Khoảng cách giữa shortest path trong sách và routing trong production không nằm ở thuật toán giỏi hơn. Nó nằm ở chỗ trong sách, chữ ngắn được định nghĩa hộ bạn. Trong đời, định nghĩa đó là việc của bạn.

“

Thuật toán tìm đường ngắn nhất luôn đúng, thứ sai là định nghĩa của chữ ngắn.

”

Bảng hệ số không ai dám xoá

Trước khi nói cách làm khác, cần công bằng với cách mà gần như mọi đội đều làm, vì nó không hề ngu ngốc.

Quỹ đạo thường bắt đầu rất hợp lý. Lấy một thư viện hoặc engine routing có sẵn, OSRM chẳng hạn, weight mặc định là khoảng cách hoặc thời gian tính theo tốc độ giới hạn của từng loại đường. Chạy được trong một sprint, demo mượt, và với thành phố vắng lúc mười giờ sáng thì kết quả thật sự tốt. Đội nào đi con đường này cũng đang làm điều hợp lý nhất với thông tin và thời gian họ có.

Rồi sai số xuất hiện, và phản xạ tự nhiên cũng xuất hiện theo. ETA thấp hơn thực tế một cách hệ thống, vậy nhân tất cả với một phẩy ba. Giờ cao điểm sai nhiều hơn, vậy sau năm giờ chiều hệ số thành một phẩy sáu. Quận trung tâm sai kiểu khác quận ven, vậy mỗi quận một hệ số riêng. Trời mưa cộng thêm mười lăm phần trăm. Mỗi hệ số đều cải thiện con số trung bình trên dashboard, và đó chính là lý do quỹ đạo này sống dai đến vậy, nó có vẻ đang hội tụ. Mỗi lần vá, sai số trung bình nhỏ đi một chút, và cảm giác chỉ còn thiếu một hệ số nữa thôi không bao giờ biến mất.

a question

Trong hệ thống bạn đang vận hành, có bao nhiêu hệ số mà không ai còn nhớ nó được thêm vào để bù cho biến nào của thực tế?

answer.Nếu phải đếm, đa số hệ thống sống đủ lâu đều có ít nhất vài hệ số như vậy. Mỗi hệ số mồ côi là một biến có thật của thực tế, giờ trong ngày, thời tiết, khu vực, đã bị nén thành một con số chết ở chỗ mô hình không đọc được, và không ai dám xoá vì không ai chứng minh được nó bù cho cái gì.
Câu hỏi này là một phép đo sức khoẻ mô hình, không phải câu đố. Nếu câu trả lời của bạn là 'không biết, phải đi hỏi', thì chính việc phải đi hỏi đã là câu trả lời.

Chỗ gãy thứ nhất là chỗ kỹ thuật. Hệ số nhân chữa được sai số tỷ lệ, không chữa được sai số cấu trúc. Hãy nhìn hai con đường giữa cùng một cặp điểm. Đường thứ nhất ngắn nhất theo mét, xuyên qua ba ngã tư phải rẽ trái không có đèn bảo vệ, mỗi cú rẽ là một lần chờ dòng xe ngược chiều hở ra một khe. Đường thứ hai dài hơn tám trăm mét, đi đường lớn, rẽ phải hai lần. Đường thứ hai nhanh hơn ở hầu hết các giờ trong ngày. Nhưng thuật toán đã chọn đường thứ nhất từ trước, dựa trên weight tính bằng mét, rồi hệ số mới được nhân lên trên thời gian của con đường đã chọn sai. Không hệ số nào nhân lên từ đường sai cho ra đường đúng. Cái sai xảy ra trước khi hệ số kịp can thiệp, ở khâu so sánh và chọn.

Chỗ gãy thứ hai sâu hơn, và nó là lý do mình kể section này bằng giọng tôn trọng thay vì chế giễu. Mỗi hệ số là một mảnh tri thức có thật về thực tế, bị nén thành một con số chết. Nhân một phẩy sáu sau năm giờ là cách nói thời gian di chuyển phụ thuộc giờ trong ngày, mà không cho mô hình biết điều đó. Cộng mười lăm phần trăm khi mưa là cách nói thời tiết là một biến, mà giấu biến đó khỏi mô hình. Vấn đề của cách mã hoá này lộ ra khi các biến gặp nhau, mưa cộng giờ cao điểm cộng quận trung tâm thì nhân kiểu gì, cộng trước hay nhân trước, và không ai trả lời được vì các hệ số chưa bao giờ được thiết kế để gặp nhau. Đến một lúc, bảng hệ số trở thành thứ không ai dám xoá dòng nào, vì không ai còn nhớ dòng đó bù cho cái gì, và xoá thử thì dashboard xấu đi ở một góc không ai ngờ.

Bảng hệ số là tri thức về thực tế bị mã hoá ở chỗ mô hình không đọc được. Vậy việc cần làm không phải là tinh chỉnh hệ số khéo hơn, mà là đưa từng biến về đúng chỗ của nó, vào trong weight. Tức là thừa nhận một điều mà đề bài trong sách đã giấu đi: weight không phải con số đến cùng với bản đồ. Weight là một mô hình, và mô hình thì phải có người thiết kế.

Weight là mô hình, không phải dữ liệu

Đề bài trong sách viết, cho đồ thị G với hàm trọng số w. Bốn chữ cho hàm trọng số là nơi toàn bộ độ khó của production được giấu đi, gọn gàng như một tấm thảm phủ lên cái hố. Ngoài đời không ai cho bạn w. Bạn phải tự trả lời w là gì, đo bằng đơn vị nào, phụ thuộc những gì, và câu trả lời đó quyết định nghiệm nhiều hơn mọi lựa chọn thuật toán phía sau cộng lại.

Nói thẳng phần bạn đã biết để gạt nó sang một bên. Dijkstra, A*, các biến thể của chúng, là tầng đáng tin nhất của cả hệ thống, được chứng minh chặt, được cài đặt kỹ trong mọi thư viện đứng đắn. Chúng gần như không bao giờ là chỗ bạn kiếm cơm. Ba quyết định dưới đây mới là.

Tốt nhất theo nghĩa nào

Quyết định thứ nhất, và nặng nhất: chọn đại lượng mà chữ tốt đo bằng, rồi thừa nhận đại lượng đó là một hàm chứ không phải một hằng số. Cùng một đoạn đường, thời gian đi qua nó lúc sáu giờ sáng và sáu giờ chiều là hai con số khác nhau, ngày thường và chủ nhật khác nhau, trời nắng và trời mưa khác nhau. Và còn một chiều ít được nói đến hơn, độ bất định. Có những con đường nhanh ở mức trung bình nhưng phương sai lớn, ngày thường trôi, nhưng một va chạm nhỏ là đứng im nửa tiếng, vì không có lối thoát nào ở giữa.

Chiều bất định đó dẫn đến một quan sát mà mình cho là đáng giá nhất của quyết định này. Business nói nhanh nhất, nhưng hành vi của họ thường đang mua ổn định nhất. Khách của app giao đồ ăn không huỷ đơn vì ETA dài. Họ huỷ vì ETA sai. Một lời hứa ba mươi lăm phút được giữ đúng giữ được khách, một lời hứa hai mươi lăm phút bị phá làm mất khách, dù hai mươi lăm nhỏ hơn ba mươi lăm. Nghĩa là hàm mục tiêu thật không phải kỳ vọng thời gian, nó là độ tin của lời hứa. Hai hàm đó cho ra hai con đường khác nhau, một hàm thích đường nhanh trung bình, một hàm thích đường ít bất ngờ.

Khi tiêu chí nhiều hơn một, nhanh, ổn định, ít tốn xăng, ít rẽ trái, bạn có hai lựa chọn lương thiện. Một là gói chúng vào một hàm vô hướng với trọng số tường minh, viết ra giấy, ai cũng đọc được, ba phần thời gian một phần phương sai chẳng hạn. Hai là chấp nhận rằng tốt nhất không tồn tại duy nhất, chỉ tồn tại những nghiệm không bị nghiệm nào khác đè hoàn toàn (dân tối ưu gọi là biên Pareto), và việc chọn trong số đó là việc của con người chứ không phải của thuật toán. Lựa chọn không lương thiện là lựa chọn phổ biến nhất, để tiêu chí ngầm trong đầu mỗi người một bản, và để thuật toán tối ưu một thứ chưa ai định nghĩa.

a question

Cho bài toán bạn đang nghĩ đến, thử viết trọn vẹn câu này ra giấy: đường A tốt hơn đường B khi và chỉ khi. Bạn có viết xong được không, và người trả tiền cho hệ thống có ký vào câu đó không?

answer.Nếu câu đó viết được trọn vẹn và người trả tiền ký vào, bạn đã có hàm mục tiêu, phần còn lại là kỹ thuật. Đa số trường hợp, câu viết dở dang ở đúng chỗ các tiêu chí va nhau, nhanh hơn nhưng phương sai lớn hơn thì sao, và chỗ dở dang đó chính là quyết định chưa được đưa ra.
Bài tập này rẻ một cách đáng ngờ, một tờ giấy và mười phút, so với chi phí của việc tối ưu một thứ chưa định nghĩa trong sáu tháng. Nếu mỗi người trong đội viết ra một câu khác nhau, đó không phải thất bại của bài tập, đó là phát hiện của nó.

Câu kiểm tra của quyết định này nằm ngay trong câu hỏi trên. Nếu câu khi và chỉ khi đó chưa viết được, mọi tinh chỉnh phía sau là tối ưu một thứ chưa được định nghĩa, và dashboard của bạn đang đo độ tiến bộ của một cuộc đi lạc.

Đồ thị của luật, không phải của bản đồ

Quyết định thứ hai: đồ thị đường phải là mô hình của luật di chuyển, không phải hình học của bản đồ. Hai thứ này trông giống nhau đến mức dễ tưởng là một. Bản đồ nói ở đây có một con đường. Luật nói ai được đi, theo hướng nào, vào giờ nào, và chuyển từ đường này sang đường kia tốn gì.

Một phần của luật sống được trên cấu trúc đồ thị quen thuộc. Đường một chiều nghĩa là edge chỉ có một hướng. Phố cấm xe tải theo giờ nghĩa là edge tồn tại có điều kiện, với xe tải thì nó biến mất khỏi đồ thị trong khung giờ cấm. Nhưng có một loại chi phí không sống được ở đâu cả trong đồ thị vẽ theo bản đồ, chi phí rẽ. Rẽ trái không đèn bảo vệ ở một ngã tư đông tốn trung bình cả phút, và cái phút đó không thuộc về đoạn đường trước ngã tư, không thuộc về đoạn đường sau ngã tư, cũng không thuộc về cái chấm hình học gọi là ngã tư. Nó là chi phí của việc chuyển từ edge này sang edge kia, một thứ mà mô hình hiện tại không có chỗ chứa.

Đến đây thì công cụ của chương ba quay lại làm việc. Khi một chi phí có thật không gắn được vào node hay edge nào hiện có, đó là tín hiệu mô hình đang thiếu một loại thực thể, và lời giải là tách quan hệ thành thực thể, reification°. Nút giao thôi là một chấm. Nó trở thành một cụm node nhỏ, mỗi node ứng với một hướng vào hoặc một hướng ra, và mỗi cách đi xuyên qua nút giao, đi thẳng, rẽ phải, rẽ trái, trở thành một edge nội bộ có weight riêng. Rẽ trái ở ngã tư X giờ là một edge, và phút chờ kia có nhà để ở. Điều đáng dừng lại một nhịp: bản đồ không đổi một mét nào, thành phố vẫn là thành phố đó, nhưng đồ thị đổi hẳn hình dạng. Đó là luận điểm của chương ba chạy bằng điện, mô hình cắt theo câu hỏi, không cắt theo hình học.

Reification, tách một quan hệ thành một thực thể có thuộc tính riêng, đã xuất hiện ở chương 3 như một quyết định mô hình hoá. Các hệ routing thực tế áp nó cho nút giao dưới nhiều tên gọi, edge-based graph hoặc turn-expanded graph, cùng một ý tưởng.

Tương tác — thử trên web
Diagram

Câu kiểm tra của quyết định này làm được ngay chiều nay. Lấy ba luật di chuyển có thật của thành phố bạn, một đường một chiều, một chỗ cấm rẽ, một khung giờ cấm, và chỉ ra mỗi luật sống ở đâu trong đồ thị của hệ thống. Luật nào không có chỗ ở là một kết quả sai đang đợi ngày phát nổ, và phần sau của chương sẽ cho thấy nó nổ kiểu gì.

Trả trước hay trả lúc hỏi

Quyết định thứ ba nói về tiền, theo nghĩa chi phí tính toán. Với đồ thị cỡ một thành phố trở lên, không ai chạy Dijkstra thuần từ con số không cho mỗi truy vấn. Câu hỏi vận hành là trả trước bao nhiêu, tính toán chuẩn bị những gì từ trước (preprocessing), để mỗi truy vấn lúc người dùng đang chờ chỉ phải trả phần còn lại.

Nếu bạn đã đọc chương trước, trade-off này có mùi quen, nó cùng họ với câu chuyện materialize, đổi bộ nhớ và công chuẩn bị lấy tốc độ lúc đọc, và cái giá thật nằm ở chỗ mọi thứ đã tính trước đều phải tính lại khi dữ liệu đổi. Ở tầng nguyên lý chỉ cần nói vậy. Cơ chế cụ thể, người ta tính trước cái gì và tại sao nó nhanh đến thế, xứng đáng được kể bằng một hệ thống thật, và đó là việc của phần ngay sau đây.

Câu kiểm tra vẫn là phép chia của chương trước, tần suất truy vấn chia tần suất dữ liệu đổi. Điểm mới của chương này là ở quy mô routing toàn cầu, phép chia đó cho ra một con số lớn đến mức câu trả lời gần như bị ép sẵn, và cái cách người ta xoay xở với phần dữ liệu đổi nhanh mới là chỗ đáng học.

Ba quyết định, và bạn để ý, không quyết định nào là thuật toán. Quyết định nặng nhất là quyết định đầu tiên, vì nó là chỗ toán học chạm vào điều business thật sự muốn, và là chỗ duy nhất mà câu trả lời sai trông giống hệt câu trả lời đúng trên dashboard. Ba hệ thống dưới đây, mỗi hệ thống bị một trong ba quyết định này thử thách đến giới hạn của nó.

Cái giá của một mili giây

Đồ thị đường của một lục địa có hàng chục triệu nút giao, và một dịch vụ bản đồ toàn cầu nhận số truy vấn tìm đường mỗi ngày ở quy mô mà chữ hàng trăm triệu là cách nói khiêm tốn. Người dùng gõ hai địa điểm cách nhau ba nghìn cây số và đợi không quá một nhịp thở. Dijkstra thuần trên đồ thị cỡ đó, cho một truy vấn xuyên lục địa, là bài toán tính bằng giây đến phút tuỳ phần cứng. Khoảng cách giữa cái người dùng đợi và cái thuật toán sách giáo khoa làm được là khoảng ba bậc độ lớn, và ba bậc độ lớn thì không lấp được bằng máy nhanh hơn.

Nó được lấp bằng một trực giác mà mọi tài xế lâu năm đều có. Đi xa thì ra đường lớn. Không ai đi từ Hà Nội vào Đà Nẵng bằng cách cân nhắc từng ngõ của từng thị trấn dọc đường, người ta đi ngõ nhà mình ra đường lớn, chạy đường lớn gần hết hành trình, rồi mới chui vào ngõ ở đầu kia. Họ hàng kỹ thuật contraction hierarchies công nghiệp hoá đúng trực giác đó. Trong giai đoạn chuẩn bị, hệ thống xếp các con đường thành tầng theo vai trò của chúng trong các hành trình dài, rồi tính sẵn những shortcut, mỗi shortcut là một edge nhân tạo tóm tắt cả một đoạn hành trình băng qua vùng đường nhỏ, kèm tổng weight đã được tính đúng. Lúc truy vấn, thuật toán từ hai đầu chỉ leo lên các tầng cao và gặp nhau ở đó, thay vì bò qua từng ngã tư của cả lục địa. Kết quả vẫn là đường đi đúng theo weight, chỉ có điều phần lớn phép cộng đã được làm từ trước.

Contraction hierarchies được mô tả trong paper của Geisberger, Sanders, Schultes và Delling (2008). Các routing engine mã nguồn mở như OSRM dùng họ kỹ thuật này, ai muốn xem cơ chế chạy thật có thể đọc code và tài liệu của chúng.

Một chú thích cần đặt ở đây cho ngay ngắn. Tầng quan trọng của đường trong kỹ thuật này là công cụ để tạo shortcut cho truy vấn nhanh, một thứ hạng phục vụ việc tính toán. Nó không phải một tuyên bố nghiêm túc rằng con đường nào quan trọng với mạng lưới, câu hỏi quan trọng theo nghĩa nghiêm túc là một loại câu hỏi khác, và nó chưa đến lượt trong chương này.

Giờ mới đến phần ăn tiền, cái giá. Toàn bộ shortcut đứng trên weight tại thời điểm chuẩn bị. Mà weight tốt, như quyết định thứ nhất đã nói, phụ thuộc giao thông, và giao thông đổi từng phút. Tính lại preprocessing cho cả lục địa thì không thể chạy từng phút được. Hai vế đó không thể cùng đúng trên một khối dữ liệu duy nhất, nên lời giải trưởng thành là không coi nó như một khối duy nhất nữa. Cắt dữ liệu theo nhịp đổi của nó. Một tầng tĩnh, hình dạng mạng đường, luật di chuyển, thứ đổi theo tuần hoặc tháng, được preprocessing sâu và đắt. Một tầng động, tình trạng giao thông hiện tại, được đắp lên lúc truy vấn hoặc được chuẩn bị nông theo chu kỳ ngắn. Đây là phép trade-off của chương trước trưởng thành thêm một bậc, câu hỏi không còn là trả trước hay trả sau cho cả hệ thống, mà là cắt dữ liệu theo đường ranh nhịp-đổi rồi trả lời riêng cho từng lớp.

Và nguyên lý cắt-theo-nhịp-đổi đó là phần chuyển giao được, kể cả khi bạn không bao giờ đụng đến đồ thị cỡ lục địa. Một đội logistics nội thành cũng có dữ liệu tĩnh, mạng đường và luật của thành phố, và dữ liệu động, tình hình kẹt xe hôm nay. Quyết định preprocessing của họ cũng nên cắt theo đường ranh đó, thay vì chọn một câu trả lời chung cho hai loại dữ liệu sống bằng hai nhịp khác nhau.

Nghiệm tối ưu mà tài xế không chạy

Hệ thống thứ hai bị thử thách ở quyết định thứ nhất, và bị thử ở chỗ không sách tối ưu nào dạy.

UPS, mỗi tài xế giao hàng mỗi ngày ghé hơn một trăm điểm dừng, và thứ tự ghé là biến số. Đây không còn là bài toán từ A đến B. Số cách xếp thứ tự một trăm điểm vượt xa mọi khả năng duyệt của mọi máy tính từng tồn tại, và ở quy mô hàng chục nghìn tuyến xe mỗi ngày, mỗi phần trăm quãng đường tiết kiệm được là một con số khổng lồ khi nhân lên. ORION, hệ thống tối ưu tuyến của UPS, ra đời cho bài toán đó, mất khoảng một thập kỷ từ nghiên cứu đến phủ toàn đội xe, và các công bố công khai của UPS dẫn mức tiết kiệm hàng trăm triệu dollar cùng hàng trăm triệu cây số chạy xe mỗi năm.

ORION được kể chi tiết trong hồ sơ giải Edelman của INFORMS (2016) và các công bố của UPS. Số liệu tiết kiệm trong đoạn trên lấy theo các nguồn này, ở mức họ công bố, người đọc muốn con số chính xác theo từng năm nên đối chiếu trực tiếp.

Nói cho ngay ngắn về ranh giới trước khi đi tiếp. Bài toán ORION tối ưu, họ hàng của traveling salesman có thêm ràng buộc đời thật, là một class khác với shortest path, có sách riêng và nghề riêng, chương này không dạy nó. Nhưng nó vẫn đứng trên đúng các tầng của chương, một đồ thị đường và một định nghĩa chi phí, rồi chồng tầng tổ hợp lên trên. Và lý do nó nằm ở đây không phải tầng tổ hợp đó. Lý do là chuyện xảy ra sau khi nghiệm tối ưu đã được tìm thấy.

Tuyến đường ORION đề xuất nhiều khi trông ngược đời với người cầm lái. Nó bảo tài xế bỏ qua một điểm giao ngay bên kia đường để vòng lại sau hai tiếng, nó vẽ những vòng cung mà mắt người nhìn vào chỉ thấy thừa. Về hàm mục tiêu, nghiệm đúng, từng con số cộng lại đều có lý. Về con mắt của một người đã chạy tuyến đó mười năm và biết tên một nửa số khách hàng, nghiệm trông ngu. Và tài xế làm điều con người làm khi được đưa một mệnh lệnh trông ngu, họ không tin, họ chạy theo cách của họ, và mọi phần trăm tối ưu trên giấy bốc hơi ngay ở bậc cửa xe tải.

Đây là khoảnh khắc quyết định thứ nhất quay lại với một bộ mặt mới. Đội ORION dần nhận ra hàm mục tiêu của họ thiếu một biến, một biến không đo bằng dặm hay phút, sự chấp nhận của người thi hành. Một nghiệm tối ưu mà không ai chạy có giá trị thực tế bằng không, nghĩa là tính chạy-được phải nằm trong định nghĩa của chữ tốt ngay từ đầu, chứ không phải là chuyện đào tạo nốt ở khâu triển khai. Những điều chỉnh sau đó của ORION, theo mức các nguồn công khai kể lại, đều xoay quanh nhận thức này, làm tuyến đường dễ hiểu và nhất quán hơn dù phải nhả lại một phần độ tối ưu, chừa cho tài xế biên độ điều chỉnh thay vì áp đặt từng bước, và đầu tư vào việc giải thích vì sao một tuyến trông vòng vèo lại nhanh hơn. Chữ tốt nhất được định nghĩa lại, từ ngắn nhất theo dặm thành ngắn trong tập những nghiệm mà con người chịu chạy.

Đặt ORION cạnh đơn hàng bị huỷ ở đầu chương và bạn thấy chúng là một lỗi, mặc hai bộ quần áo. ETA hai mươi lăm phút tối ưu trên một thành phố không có thật, sai số nằm giữa mô hình và mặt đường. Tuyến đường tối ưu trên một đội xe không có thật, nơi tài xế là máy thi hành không có mắt, sai số nằm giữa mô hình và lòng tin. Cả hai đều là hàm mục tiêu bỏ quên một phần của thế giới, và cả hai đều không sửa được bằng thuật toán tốt hơn.

“

Nghiệm tối ưu mà không giải thích được là nghiệm không được dùng, và tính chạy-được phải nằm trong định nghĩa của chữ tốt, không phải trong tài liệu đào tạo.

”

Khi weight tĩnh hết khả năng

Còn một đầu nữa của quyết định thứ nhất, đầu bên kia, nơi hàm weight được thiết kế chu đáo đến mấy cũng đuối.

Đi theo đúng tinh thần của chương, bạn sẽ xây hàm thời gian phụ thuộc giờ trong ngày, rồi thêm ngày trong tuần, rồi thời tiết, rồi sự kiện, loại xe, thói quen của chính tài xế. Mỗi biến đều có thật và đều đáng đưa vào. Nhưng số tương tác giữa các biến tăng theo cấp số, mưa lúc tan tầm khác mưa lúc nửa đêm, và đến một độ phức tạp nào đó, bảng hệ số không ai dám xoá của phần đầu chương quay lại, lần này dưới hình dạng tinh vi hơn, một hàm thiết kế tay với hàng trăm tham số mà không ai dám đụng. Cùng một căn bệnh, nhà mới.

Các hệ ETA hiện đại xử lý điểm đuối này bằng một bước đi nghe to tát nhưng cấu trúc lại rất khiêm tốn, coi việc ước lượng thời gian là bài toán dự đoán và để mô hình học từ dữ liệu chuyến đi thật. Chi tiết đáng giá nhất với chương này nằm ở chỗ hệ thống không vứt đồ thị đi. Trong kiến trúc mà Uber công bố cho DeepETA, tầng routing trên đồ thị đường vẫn chạy trước, cho ra đường đi và một ETA thô đúng theo nghĩa của chương này, rồi tầng học máy đứng sau, sửa ETA thô bằng những tín hiệu mà đồ thị không nhìn thấy được. Đồ thị cho cấu trúc, mô hình học điền con số. Khung xương và phần mềm của cùng một cơ thể.

Kiến trúc hai tầng, routing engine cộng model° hậu chỉnh, được mô tả trong bài DeepETA trên blog kỹ thuật của Uber (2022). Chi tiết bên trong model° nằm ngoài phạm vi chương này.

Đặt vào ngôn ngữ của chương thì đây vẫn là quyết định thứ nhất, định nghĩa weight, chỉ là câu trả lời đã tiến hoá qua ba đời, một con số chết, một hàm thiết kế tay, một hàm được học. Trục tiến hoá đó không xoá hai quyết định còn lại, đồ thị vẫn phải chứa đúng luật di chuyển, và chi phí tính toán vẫn phải được trả ở đâu đó, trước hay sau. Còn chuyện đồ thị và máy học lồng vào nhau đến đâu, câu chuyện đó dài hơn một section, và nó sẽ quay lại khi series đi đến chỗ những mô hình ngôn ngữ gặp đồ thị. Ở đây chỉ cần một quan sát, chúng không thay thế nhau. Chúng chia việc.

❦

Hai cách hỏng, một câu hỏi mua hay xây

Phần cuối của chương dành cho judgment, bắt đầu bằng hai cách hỏng mà mình thấy lặp lại nhiều nhất, và kết bằng một lời khuyên có vị ngược hẳn chương trước.

Cách hỏng thứ nhất, tối ưu con số đo được thay vì con số khách quan tâm. Nó trông như thế này, dashboard ETA trung bình đẹp dần qua từng quý, đội kỹ thuật có biểu đồ để khoe trong mọi buổi review, và tỷ lệ huỷ đơn không nhúc nhích, mọi người họp mỗi tuần để hỏi nhau tại sao. Tại sao của nó nằm ở quyết định thứ nhất bị làm theo hướng thuận tay, kỳ vọng dễ đo, dễ làm baseline, dễ vẽ, còn độ tin của lời hứa khó đo hơn một bậc, thế là đội tối ưu kỳ vọng trong khi khách trừng phạt phương sai, hai con số sống hai cuộc đời riêng. Cách bắt, định kỳ đặt con số đang tối ưu cạnh con số hành vi, huỷ đơn, khiếu nại, tài xế tự ý bỏ tuyến, và nếu hai đường cong không nói chuyện với nhau qua nhiều quý, hàm mục tiêu của bạn đang nói chuyện với chính nó.

Cách hỏng thứ hai, đồ thị sai làm thuật toán đúng cho ra kết quả sai. Nó trông như thế này, tuyến đường đề xuất đi ngược một đoạn vừa đổi thành một chiều tháng trước, dẫn xe tải vào phố cấm giờ, hoặc tự tin băng qua cây cầu đã đóng sửa ba tuần. Người dùng không nói đồ thị của các bạn cũ, người dùng nói app này ngu, và họ đúng theo cách duy nhất mà người dùng cần đúng. Tại sao của nó có hai tầng, tầng thứ nhất là tầng đồ thị mục theo thời gian thật, họ hàng với chuyện ảnh chụp và phim của chương trước, tầng thứ hai hiểm hơn, lỗi của tầng dữ liệu mặc đồng phục của lỗi tầng thuật toán, người dùng báo app chỉ đường ngu và đội kỹ thuật đi debug thuật toán hàng tuần liền, đúng cái bẫy mà cuộc điều tra ở đầu chương đã tháo ngòi. Cách bắt, tách kiểm thử theo tầng, một bộ test riêng cho câu hỏi đồ thị có phản ánh luật hiện hành không, đối chiếu nguồn dữ liệu đường chính thức và report người dùng, chạy độc lập với mọi test thuật toán, và đo tuổi của dữ liệu đồ thị như một metric vận hành hạng nhất, không phải một ghi chú.

Còn when to deviate của chương này, nó có vị ngược với chương trước, và cái vị đó đáng được nói thẳng. Với reachability°, tự viết một recursive CTE° là lành mạnh, chương trước đã bảo vệ điều đó. Với routing trên đường thật, tự xây gần như luôn là sai. Đồ thị đường của cả thế giới, dữ liệu giao thông thời gian thực, luật di chuyển của từng thành phố đổi từng tuần theo từng quyết định hành chính, đó là sản phẩm trọn đời của những đội hàng trăm người, và các dịch vụ routing bán đúng thứ đó qua một API. Câu kiểm tra duy nhất, routing có phải core business của bạn không, nghĩa là lợi thế cạnh tranh của công ty có nằm ở chỗ tìm đường tốt hơn đối thủ không. Gọi xe và giao hàng ở quy mô lớn, logistics chặng cuối, câu trả lời có thể là có, và chính những công ty đó mới nắm dữ liệu chuyến đi riêng đủ dày để làm weight tốt hơn nhà cung cấp chung. Còn lại, gần như chắc chắn là không.

Nhưng mua không miễn trừ ba quyết định. API trả về đường tốt theo định nghĩa của họ, với weight của họ, trên đồ thị của họ. Bạn vẫn phải biết định nghĩa tốt của mình là gì để chọn đúng tham số, vẫn phải kiểm tra đồ thị của họ có chứa luật địa phương mà nghiệp vụ của bạn va phải không, xe tải của bạn có bị họ dẫn vào phố cấm giờ không. Toàn bộ chương này, nếu bạn không bao giờ tự xây một engine routing nào, vẫn nguyên giá trị ở đúng chỗ đó, nó là danh sách những câu phải hỏi nhà cung cấp. Mua lời giải vẫn phải sở hữu bài toán.

Quay lại dashboard chiều thứ sáu một lần. Hệ thống giờ đã khác, weight là hàm của giờ và của mưa, ngã tư rẽ trái đã có giá riêng của nó, ETA hứa ba mươi lăm phút và giữ được ba mươi lăm phút, đường cong huỷ đơn chiều thứ sáu trông lại giống một đường cong. Câu hỏi từ A đến B, coi như đã có lời đáp đáng tin.

Rồi nhìn cả thành phố lúc tan tầm, không nhìn một chuyến đi nào nữa mà nhìn tất cả cùng lúc, bạn sẽ thấy một điều mà không truy vấn A đến B nào hiển thị được. Có những nút giao mà mọi con đường tốt đều muốn chen qua, hàng nghìn nghiệm tối ưu của hàng nghìn chuyến đi khác nhau cùng đặt cược vào một cây cầu. Khi cây cầu đó nghẹt, không phải một tuyến đường chậm đi, cả thành phố đổi hình dạng. So sánh các con đường là một chuyện. Còn một vị trí trong mạng nặng đến đâu, đó là loại câu hỏi mà chương này chưa có tên để gọi.

✱ hết bản in thử