Zamiana notatnika Jupyter na Markdown: zostaje narracja, znika opakowanie
Notatnik to dwa dokumenty pod jedną nazwą pliku. Jest to, co napisałeś — nagłówki, wyjaśnienia, kod, wypisany wynik, który był całą pointą — i jest koperta JSON, w której format to przechowuje. Otwórz .ipynb w edytorze tekstu, a zobaczysz kopertę: każdy wiersz prozy rozbity na osobny łańcuch znaków, "execution_count": 7 przy każdej komórce, wszędzie puste obiekty "metadata": {}, a gdzieś pośrodku ćwierć megabajta base64, które renderuje się jako jeden mały wykres punktowy.
Konwersja na Markdown wyrzuca kopertę i zostawia dokument. Komórki markdown już były Markdownem, więc przechodzą nietknięte. Komórki z kodem stają się blokami w ogrodzeniu, opisanymi językiem jądra — czyli w formie, którą rozumie już każdy model, każdy README i każdy generator stron statycznych. Upuść .ipynb powyżej, a konwersja wykona się w tej karcie przeglądarki: plik nigdy nie jest wysyłany.
Gdzie naprawdę siedzi ciężar
Ludzie zakładają, że duży notatnik jest duży przez kod. Prawie nigdy tak nie jest. Ciężar siedzi w czterech miejscach, w kolejności malejącej:
- Obrazy wyjściowe w base64. Każde
plt.show()zapisuje wyrenderowany PNG do pliku jako tekst base64. Jeden wykres to zwykle 200–400 KB znaków. Notatnik z tuzinem wykresów w większości składa się z wykresów. Dla kogoś, kto czyta tekst, nic z tego nic nie znaczy. - Reprezentacje HTML ramek danych. Pandas zwraca dla tego samego wyniku zarówno
text/plain, jak itext/html. Wersja HTML ciągnie ze sobą style inline i<table>ze znacznikiem na każdą komórkę, więc bywa dwadzieścia razy większa od wersji tekstowej, mówiąc dokładnie to samo. - Klatki pasków postępu. Pętla z tqdm zapisuje nowy wiersz przy każdym odświeżeniu, rozdzielony powrotami karetki. Plik kończy z setkami kopii paska, który wyświetlił się tylko raz.
- Metadane każdej komórki. Z osobna błahe, razem już nie: identyfikatory komórek, liczniki wykonań, flagi zwinięcia i puste obiekty metadanych na kilkuset komórkach sumują się w coś zauważalnego.
Konwersja powyżej radzi sobie z każdym z tych punktów. Ładunki multimedialne są liczone i zastępowane jednym wierszem znacznika, żebyś nadal widział, gdzie stał wykres. Gdy wynik oferuje i czysty tekst, i HTML, wygrywa czysty tekst. Sekwencje powrotów karetki zwijają się do końcowego stanu wiersza. Metadane znikają w całości. Narzędzie raportuje, co usunęło, więc oszczędność jest widoczna, a nie deklarowana.
Dlaczego bloki w ogrodzeniu znaczą więcej, niż się wydaje
Najbardziej użyteczne, co Markdown robi dla notatnika, to jawne oznaczenie granicy między prozą a kodem. W surowym JSON-ie ta granica istnieje wyłącznie jako pole "cell_type" kilka wierszy nad treścią. Spłaszczone niedbale, wyjaśnienie i kod, który opisuje, zlewają się w jedno, a czytelnik — człowiek czy model — musi z samej składni wywnioskować, co jest czym.
Ogrodzenie opisane językiem usuwa to zgadywanie. Przenosi też dalej język jądra: konwerter czyta language_info z metadanych notatnika, a w razie braku sięga po kernelspec, więc notatnik w R albo Julii dostaje etykietę R albo Julia, zamiast po cichu zostać Pythonem. Wyjścia dostają własne ogrodzenie bez etykiety, pod komórką, która je wyprodukowała, poprzedzone zwykłym wierszem Output:, co utrzymuje widoczny związek przyczynowy bez wymyślania składni, której Markdown nie ma.
Ślady stosu warto zachować
Kusi, żeby wyrzucić wyjście błędów razem z całą resztą. Zwykle to zły ruch. Jeśli podajesz notatnik modelowi z pytaniem, dlaczego komórka się wywala, to ślad stosu jest całym pytaniem. Tym, co czyni ślady nieprzyjemnymi w surowej postaci, nie jest treść, tylko kody kolorów ANSI, w które IPython je opakowuje — sekwencje sterujące, które w terminalu renderują się jako czytelny kolor, a wszędzie indziej jako śmieci w rodzaju ESC[0;31m.
Te sekwencje są usuwane, a tekst śladu zostaje. Długie skracane są od środka, a nie od końca, bo w głębokim stosie użyteczne są pierwsze i ostatnie ramki, a dwieście wierszy bibliotecznych wnętrzności pomiędzy nimi to dokładnie to, co i tak byś przeskoczył.
Notatniki w zrzucie repozytorium
Ta sama konwersja działa wewnątrz narzędzi do GitHuba, GitLaba i katalogu lokalnego na tej stronie. Zaznacz .ipynb w repozytorium, a trafi do wyniku jako czytelne komórki, a nie ściana JSON-a. To znaczy więcej, niż brzmi: w repozytorium do analizy danych to często właśnie w notatnikach mieszka całe rozumowanie, a do tej pory były to pliki, które trzeba było odznaczyć, żeby wynik pozostał użyteczny.
W tym kontekście limit wyjścia jest ciaśniejszy niż na tej stronie, bo zrzut repozytorium to kontekst dla modelu, a wyjście komórek jest w nim najmniej wartościową częścią w przeliczeniu na token. Notatka w przekonwertowanym tekście zapisuje, ile usunięto, więc nic nie znika po cichu.
Starsze notatniki nadal działają
Wersja 4 formatu trzyma komórki w tablicy cells na najwyższym poziomie. Wersja 3, wciąż porozrzucana po publicznych repozytoriach z początku lat dziesiątych, zagnieżdża je o poziom głębiej w worksheets i nazywa pole źródłowe input zamiast source. Obie postacie są odczytywane. Tak samo typ wyjścia pyout, którego wersja 3 używała tam, gdzie wersja 4 mówi execute_result.
Dwóch rzeczy świadomie się nie podejmuje. Widżety notatnika — interaktywne suwaki i wykresy oparte na ipywidgets — trzymają swój stan w osobnym bloku metadanych i jako tekst nie dają nic użytecznego; znacznik mówi ci tylko, że któryś tam był. A kolejność wykonania komórek zachowana jest taka, jaka jest w pliku, bez sortowania po liczniku wykonań, bo kolejność, w jakiej czyta się notatnik, to kolejność, w jakiej został napisany.
Nic nie jest wysyłane
Notatniki niosą rzeczy, o których się zapomina: klucz API wklejony do komórki podczas debugowania, łańcuch połączenia do bazy, wycinek danych produkcyjnych wypisany, żeby sprawdzić złączenie. Konwersja działa w JavaScripcie w tej karcie. Nie ma kroku wysyłki, nie ma kopii na serwerze i nie ma czego potem kasować. Zamknij kartę i po sprawie.
Jeśli wolisz zwykły tekst od Markdowna — bez ogrodzeń, bez struktury, sama proza i kod — to daje konwerter notatnika na tekst, a wybrany plik przechodzi razem z tobą.