Chuyển notebook Jupyter sang Markdown — miễn phí và riêng tư

Biến notebook .ipynb thành Markdown gọn gàng ngay trong trình duyệt. Giữ phần chữ và mã, bỏ ảnh base64 và siêu dữ liệu thực thi vốn làm phình tệp JSON gốc.

Chuyển Jupyter Notebook sang Markdown: giữ phần nội dung, bỏ lớp vỏ

Một notebook thực chất là hai tài liệu nằm chung một tên tệp. Có thứ bạn viết ra — tiêu đề, phần giải thích, mã nguồn, kết quả in ra để chứng minh luận điểm — và có lớp vỏ JSON mà định dạng này dùng để cất tất cả vào. Mở một tệp .ipynb bằng trình soạn thảo văn bản là thấy ngay lớp vỏ đó: mỗi dòng văn xuôi bị tách thành một chuỗi riêng, "execution_count": 7 ở mọi ô, các đối tượng "metadata": {} rỗng rải khắp nơi, và đâu đó ở giữa là một phần tư megabyte base64 chỉ để hiện ra một biểu đồ phân tán bé xíu.

Chuyển sang Markdown là vứt lớp vỏ đi và giữ lại tài liệu. Các ô markdown vốn đã là Markdown nên đi thẳng qua, không đụng chạm gì. Ô mã trở thành khối mã có hàng rào kèm nhãn ngôn ngữ của kernel — đúng dạng mà mọi mô hình, mọi README và mọi trình sinh trang tĩnh đều đã hiểu sẵn. Thả một tệp .ipynb vào ô phía trên và việc chuyển đổi diễn ra ngay trong tab trình duyệt này; tệp không bao giờ được tải lên đâu cả.

Sức nặng thật sự nằm ở đâu

Ai cũng nghĩ notebook nặng là vì mã nguồn. Gần như không bao giờ. Sức nặng nằm ở bốn chỗ, xếp theo thứ tự giảm dần:

  • Ảnh xuất ra dưới dạng base64. Mỗi lệnh plt.show() ghi thẳng ảnh PNG đã kết xuất vào tệp dưới dạng văn bản base64. Một hình thường ngốn 200 đến 400 KB ký tự. Một notebook có chục biểu đồ thì phần lớn dung lượng chính là biểu đồ. Với người đọc bản văn bản, chỗ đó chẳng mang thông tin gì.
  • Bản HTML của dataframe. Pandas xuất ra cả text/plain lẫn text/html cho cùng một kết quả. Bản HTML kéo theo style nội tuyến và một <table> với một thẻ cho mỗi ô, nên nó có thể to gấp hai mươi lần bản thuần văn bản trong khi nói đúng y một điều.
  • Các khung hình của thanh tiến trình. Một vòng lặp tqdm ghi một dòng mới sau mỗi lần làm mới, ngăn cách bằng ký tự về đầu dòng. Kết cục là tệp chứa hàng trăm bản sao của một thanh tiến trình mà người dùng chỉ nhìn thấy đúng một lần.
  • Metadata của từng ô. Riêng lẻ thì không đáng kể, gộp lại thì có: id ô, số lần thực thi, cờ thu gọn và những đối tượng metadata rỗng, nhân với vài trăm ô là thành một con số thật.

Bộ chuyển đổi phía trên xử lý từng khoản một. Dữ liệu ảnh được đếm rồi thay bằng một dòng đánh dấu duy nhất, để bạn vẫn biết chỗ nào từng có hình. Khi một kết quả có cả bản thuần văn bản lẫn bản HTML, bản thuần văn bản thắng. Chuỗi ký tự về đầu dòng được rút về trạng thái cuối cùng của dòng. Metadata bị bỏ hẳn. Công cụ báo lại nó đã gỡ những gì, nên phần tiết kiệm được là thứ nhìn thấy chứ không phải lời hứa suông.

Vì sao hàng rào mã quan trọng hơn vẻ ngoài của nó

Điều hữu ích nhất mà Markdown làm cho một notebook là đánh dấu rạch ròi ranh giới giữa văn xuôi và mã. Trong JSON thô, ranh giới đó chỉ tồn tại dưới dạng một trường "cell_type" nằm cách nội dung vài dòng phía trên. Làm phẳng cẩu thả thì phần giải thích và đoạn mã mà nó nói tới dính liền vào nhau, và người đọc — dù là người hay mô hình — phải tự đoán đâu là gì, chỉ dựa vào cú pháp.

Một hàng rào có gắn nhãn ngôn ngữ xóa bỏ chuyện đoán mò đó. Nó cũng mang theo ngôn ngữ của kernel: bộ chuyển đổi đọc language_info trong metadata của notebook, không có thì lùi về kernelspec, nên một notebook R hay Julia được rào là R hoặc Julia thay vì bị âm thầm dán nhãn Python. Kết quả xuất ra nằm trong hàng rào riêng, không nhãn, ngay dưới ô đã sinh ra nó, mở đầu bằng một dòng Output: đơn giản — đủ để giữ quan hệ nhân quả hiện rõ mà không phải bịa thêm cú pháp Markdown vốn không có.

Traceback đáng được giữ lại

Có một cám dỗ là gỡ luôn phần lỗi cùng với mọi thứ khác. Thường thì đó là quyết định sai. Nếu bạn đưa notebook cho một mô hình và hỏi vì sao một ô chạy hỏng, thì traceback chính là toàn bộ câu hỏi. Thứ khiến traceback khó chịu ở dạng thô không phải nội dung mà là mã màu ANSI mà IPython bọc quanh nó — những chuỗi thoát hiện thành màu dễ đọc trong terminal và thành rác ESC[0;31m ở mọi nơi khác.

Các chuỗi đó bị lột bỏ, phần chữ của traceback được giữ. Traceback quá dài thì bị cắt bớt ở giữa chứ không cắt đuôi, bởi trong một ngăn xếp sâu thì những khung hữu ích là vài khung đầu và vài khung cuối, còn hai trăm dòng nội bộ thư viện nằm giữa vốn dĩ là phần bạn sẽ bỏ qua.

Notebook trong bản kết xuất kho mã

Chính phép chuyển đổi này cũng chạy bên trong các công cụ GitHub, GitLab và thư mục cục bộ trên trang. Chọn một tệp .ipynb trong kho mã và nó rơi vào kết quả dưới dạng những ô dễ đọc chứ không phải một bức tường JSON. Chuyện này quan trọng hơn vẻ ngoài của nó: trong một kho mã khoa học dữ liệu, notebook thường là nơi chứa phần lập luận thực sự, mà trước đây chúng lại đúng là những tệp bạn buộc phải bỏ chọn để kết quả còn dùng được.

Trong bối cảnh đó, giới hạn kết quả chặt hơn so với trang này, vì bản kết xuất kho mã là ngữ cảnh cho mô hình, và tính trên mỗi token thì phần kết quả của ô là phần ít giá trị nhất. Một ghi chú trong văn bản đã chuyển đổi cho biết đã cắt bỏ bao nhiêu, nên không có gì biến mất trong im lặng.

Notebook đời cũ vẫn chuyển sang Markdown được

Định dạng phiên bản 4 đặt các ô trong một mảng cells ở cấp cao nhất. Phiên bản 3, thứ vẫn còn nằm rải rác trong các kho mã công khai từ đầu thập niên 2010, lồng chúng sâu thêm một tầng bên trong worksheets và đặt tên trường nguồn là input thay vì source. Cả hai hình dạng đều đọc được. Kiểu kết quả pyout mà phiên bản 3 dùng ở chỗ phiên bản 4 ghi execute_result cũng vậy.

Có hai thứ cố tình không làm. Widget của notebook — thanh trượt và biểu đồ tương tác dựa trên ipywidgets — lưu trạng thái trong một khối metadata riêng và kết xuất ra chữ thì chẳng còn gì dùng được; dòng đánh dấu chỉ nói cho bạn biết chỗ đó từng có một widget. Và thứ tự các ô được giữ đúng như nó nằm trong tệp chứ không sắp lại theo số lần thực thi, bởi thứ tự bạn đọc một notebook là thứ tự nó được viết ra.

Không có gì được tải lên

Notebook mang theo những thứ người ta quên mất: một khóa API dán vào ô lúc gỡ lỗi, một chuỗi kết nối cơ sở dữ liệu, một lát dữ liệu production in ra để kiểm tra phép join. Việc chuyển đổi chạy bằng JavaScript ngay trong tab này. Không có bước tải lên, không có bản sao trên máy chủ, và cũng không có yêu cầu xóa nào phải gửi sau đó. Đóng tab là xong.

Nếu bạn muốn văn bản thuần thay vì Markdown — không hàng rào, không cấu trúc, chỉ còn phần chữ và phần mã — thì công cụ chuyển notebook sang văn bản làm đúng việc đó, và tệp bạn đang chọn sẽ được mang sang.