Fəsil 01 · 8 dəq oxu

Data science nədir: workflow və notebook mühiti

Data science nədir: workflow və notebook mühiti

Data science haqqında söhbət çox vaxt alətlərdən başlayır: hansı kitabxana, hansı model, hansı GPU. Amma alət cavabdır, sual deyil. Bu fəsil əvvəlcə sualı qoyur: data science nədir, hansı mərhələlərdən keçir, Python bu sahənin ortaq dili necə oldu. Sonra kitab boyu içində işləyəcəyimiz mühitə, IPython və Jupyter notebook-a keçir. Notebook-u yalnız kod yazılan pəncərə kimi görmək onun imkanlarının çoxunu kənarda qoyur: help sistemi, magic command-lər, shell inteqrasiyası, debugger və profiler. Bunları öyrənməyə bir saat ayırmaq sonrakı hər fəsildə saatlar qazandırır.

Data science üç sahənin kəsişməsidir

Data science yeni elm deyil, üç köhnə bacarığın birləşməsidir. Statistika data-dan nəticə çıxarmağın və bu nəticənin nə qədər etibarlı olduğunu ölçməyin dilidir. Computer science həmin hesablamaları real ölçülü data üzərində icra etməyin yoludur: alqoritmlər, yaddaş, paralel hesablama. Domain expertise isə hansı sualın mənalı olduğunu və cavabın nə demək olduğunu bilməkdir. Bu üçlükdən biri çatışmayanda nəticə ya statistik cəhətdən səhv, ya hesablama baxımından mümkünsüz, ya da heç kimə lazım olmayan bir şey olur.

Praktikada data scientist-in işinin böyük hissəsi model qurmaq deyil. Tipik workflow belə görünür:

  1. Sual — nəyi bilmək istəyirik və cavab hansı qərarı dəyişəcək?
  2. Əldə etmə — data haradadır: fayl, verilənlər bazası, API, sensor?
  3. Təmizləmə — missing dəyərlər, dublikatlar, səhv formatlar, uyğunsuz vahidlər.
  4. Exploration — paylanmalara, korrelyasiyalara, anomaliyalara baxmaq.
  5. Modelləşdirmə — statistik model və ya machine learning alqoritmi.
  6. Kommunikasiya — qrafiklər, hesabat, dashboard; nəticəni qərar verənə çatdırmaq.

Bu mərhələlər xətti deyil, dövridir. Exploration sualı dəyişdirir, model təmizləmədə buraxılan səhvi üzə çıxarır. Təcrübəli mütəxəssislər vaxtlarının yarıdan çoxunu ilk dörd mərhələyə sərf edir və bu kitabın strukturu da həmin reallığı əks etdirir.

Python niyə ortaq dil oldu

Python bu sahə üçün yaradılmayıb. Onun qalib gəlməsinin səbəbi dilin özündən çox ətrafında yaranan ekosistemdir. Dörd kitabxana bu ekosistemin onurğasıdır və kitabın hissələri də onlara uyğundur:

KitabxanaRoluBu kitabda
NumPySıx, eyni tipli ədəd array-ləri və onlar üzərində sürətli əməliyyatlarFəsil 2
pandasEtiketli, müxtəlif tipli cədvəl data-sı: DataFrameFəsil 3–4
MatplotlibVizuallaşdırmaFəsil 5
scikit-learnMachine learning alqoritmlərinin vahid interfeysiFəsil 6–8

Bu dördlüyün ortaq xüsusiyyəti var: hamısı NumPy array-i üzərində qurulub. pandas DataFrame-i içində NumPy array-ləri saxlayır, Matplotlib array-ləri çəkir, scikit-learn array-ləri qəbul edib array qaytarır. Bu ortaq data strukturu kitabxanaların bir-birinə asanlıqla qoşulmasını təmin edir və ona görə kitab NumPy ilə başlayır.

Python-un özü yavaş interpreted dildir. Amma ağır hesablama C və Fortran ilə yazılmış kitabxanaların içində gedir, Python isə həmin hesablamaları bir-birinə bağlayan, oxunaqlı "yapışqan" rolunu oynayır. Bu bölgü, yəni Python-da düşünüb C-də hesablamaq, növbəti fəsildə vectorization-ı izah edəndə yenidən qarşımıza çıxacaq.

IPython və Jupyter: exploration üçün REPL

Data ilə işləmək skript yazmaqdan fərqlidir. Skript əvvəlcədən bilinən addımları icra edir; exploration isə hər addımın nəticəsinə baxıb növbətini seçməkdir. Bunun üçün REPL lazımdır: read-eval-print loop, yəni bir ifadəni oxu, hesabla, nəticəni göstər, təkrarla.

IPython Python-un standart REPL-inin zənginləşdirilmiş versiyasıdır. Jupyter isə IPython-u brauzerdə işləyən notebook formatına salır: kod, nəticə, qrafik və izahat mətni eyni sənəddə, hüceyrələr şəklində. Aşağıda danışacağımız imkanların hamısı IPython-a aiddir və həm terminalda, həm də notebook-da eyni şəkildə işləyir.

Help və documentation

IPython-da sənədə baxmaq üçün brauzerə getmək lazım deyil. Obyektin adının sonuna ? əlavə etsəniz docstring-i, ?? əlavə etsəniz isə mənbə kodunu göstərir:

len?          # docstring
np.sum??      # mənbə kodu (Python-da yazılıbsa)

?? C-də yazılmış funksiya üçün mənbə göstərə bilmir və ? kimi davranır. Bu, kitabxananın hansı hissəsinin Python, hansının C olduğunu bilmək üçün ucuz üsuldur.

Tab completion obyektin atribut və metodlarını sadalayır: df.<TAB> DataFrame-in bütün metodlarını, df.gr<TAB> isə gr ilə başlayanları göstərir. Alt xətt ilə başlayan "private" adlar yalnız _ yazıb TAB basanda görünür. Metodun adının yalnız ortasını xatırlayırsınızsa, wildcard axtarışı kömək edir:

str.*find*?   # str-in adında "find" olan bütün metodları

Bu üç alət birlikdə sənədi oxumaq əvəzinə kitabxananı "dialoq" ilə öyrənməyə imkan verir.

Magic command-lər

Magic command-lər IPython-un Python sintaksisi üzərinə əlavə etdiyi qısa əmrlərdir. % ilə başlayanlar bir sətirə, %% ilə başlayanlar bütün hüceyrəyə tətbiq olunur:

%run script.py        # faylı icra et, onun funksiyaları session-a gəlir
%timeit sum(range(1000))
# 14.2 µs ± 0.3 µs per loop

%%timeit
total = 0
for i in range(1000):
    total += i

%run skriptdəki funksiya və dəyişənləri cari session-a gətirir, yəni faylda yazdığınız kodu notebook-da interaktiv sınamaq mümkün olur. %history session-da icra edilmiş əmrlərin siyahısını verir; %history -n 1-5 kimi aralıq göstərmək və -f ilə fayla yazmaq olur. %matplotlib inline qrafiklərin ayrı pəncərədə deyil, notebook-un içində görünməsini təmin edir. Bütün magic-lərin siyahısı %lsmagic, hər birinin sənədi isə %timeit? kimi həmin ? ilə alınır.

Giriş və çıxış tarixçəsi

IPython hər hüceyrənin girişini və çıxışını yadda saxlayır. In icra edilmiş kodların list-i, Out isə nəticələrin dict-idir:

In [1]: import math
In [2]: math.sin(2)
Out[2]: 0.9092974268256817
In [3]: Out[2] ** 2 + len(In[1])   # In[1] string, Out[2] ədəddir

Sonuncu nəticə _, ondan əvvəlki __, daha əvvəlki ___ ilə əldə olunur; _2 isə Out[2]-nin qısaltmasıdır. Bu, uzun hesablamanın nəticəsini dəyişənə yazmağı unudanda xilas edir.

Bu tarixçənin gizli qiyməti var: Out-da saxlanılan hər nəticə yaddaşda qalır. Böyük DataFrame-i hüceyrənin sonunda göstərib keçsəniz, həmin DataFrame siz onu dəyişən kimi silsəniz belə Out-da yaşamağa davam edir. Yaddaş azalanda del Out[5] və ya %reset -f out kömək edir.

Bir detal: Out yalnız nəticə qaytaran ifadələri saxlayır, print isə nəticə qaytarmır. Nəticənin göstərilməsini və Out-a yazılmasını istəmirsinizsə, sətri ; ilə bitirin. Qrafik çəkəndə faydalıdır, çünki Matplotlib funksiyaları çox vaxt lazımsız obyekt qaytarır:

plt.plot(x, y);   # obyektin təsviri çap olunmur

Shell əmrləri

Fayl sistemində gəzmək üçün notebook-dan çıxmağa ehtiyac yoxdur. ! ilə başlayan sətir shell-ə göndərilir və nəticə Python dəyişəninə yazıla bilər:

!ls data/
files = !ls data/*.csv     # IPython-un xüsusi list tipi
!echo {len(files)}         # Python dəyişənini shell-ə ötürmək

Bir tələ: !cd data işləmir, çünki hər ! əmri ayrı alt-process-də icra olunur və cari qovluq dəyişikliyi process bitəndə itir. Qovluğu dəyişmək üçün %cd magic-i lazımdır. Automagic aktiv olanda (default belədir) %cd, %ls, %pwd kimi əmrləri % olmadan da yazmaq olur, yetər ki, eyni adlı Python dəyişəni olmasın.

Xətalar və debugging

Xəta baş verəndə IPython traceback göstərir. Onun uzunluğunu %xmode tənzimləyir: Plain qısa, Context default, Verbose isə hər frame-dəki dəyişənlərin dəyərlərini də göstərir. Çox vaxt Verbose xətanın səbəbini traceback-in özündən oxumağa imkan verir.

Bu kifayət etmədikdə %debug sonuncu xətanın baş verdiyi yerdə interaktiv debugger açır. Orada print ilə dəyişənlərə baxmaq, up və down ilə call stack-də gəzmək, quit ilə çıxmaq olur. Hər xətada debugger-in avtomatik açılmasını istəyirsinizsə, %pdb on yazın. Bu, print səpələməkdən qat-qat sürətli üsuldur, çünki xəta anındakı real vəziyyəti görürsünüz.

Vaxt ölçmə və profiling

Optimallaşdırmanın birinci qaydası: ölçmədən optimallaşdırma. İntuisiya darboğazı tapmaqda təəccüblü dərəcədə pis işləyir.

%time ifadəni bir dəfə icra edib vaxtı göstərir. %timeit isə dəfələrlə icra edib ortalama və standart sapma verir, ona görə qısa ifadələr üçün daha etibarlıdır. Amma %timeit-in tələsi var: eyni list-i təkrar-təkrar sort etmək kimi bir dəfədən sonra ucuzlaşan əməliyyatlar üçün %time daha düzgün nəticə verir.

Hansı funksiyanın vaxt apardığını %prun göstərir: hər çağırılan funksiya üzrə ümumi vaxt və çağırış sayı. Hansı sətirin vaxt apardığını isə %lprun göstərir; bu, line_profiler extension-ını tələb edir:

%prun slow_function(10_000)
%load_ext line_profiler
%lprun -f slow_function slow_function(10_000)

Yaddaş üçün də eyni cütlük var: %memit bir ifadənin pik yaddaşını, %mprun isə faylda yazılmış funksiyanın sətir-sətir yaddaş profilini göstərir (memory_profiler extension-ı ilə). %mprun yalnız fayldan import olunmuş funksiyalar üçün işləyir, notebook-da yazılmış funksiya üçün yox.

Qərar vermə ardıcıllığı belədir: əvvəlcə %time ilə ümumi vaxtı ölçün, sonra %prun ilə hansı funksiyanın baha olduğunu tapın, sonra %lprun ilə həmin funksiyanın içindəki sətri tapın. Yalnız bundan sonra kodu dəyişin və yenidən ölçün. Yaddaş problemi varsa, eyni zəncir %memit və %mprun ilə gedir.

Notebook gigiyenası

Notebook-un ən güclü xüsusiyyəti, hüceyrələri istənilən ardıcıllıqla icra etmək, eyni zamanda ən böyük tələsidir. Hüceyrə 7-ni dəyişib yenidən icra edəndə hüceyrə 5 köhnə nəticəni saxlayır. Yuxarıdakı hüceyrəni silsəniz, orada yaradılmış dəyişən yaddaşda qalır və kod işləməyə davam edir, amma notebook-u yenidən açanda işləmir. Hüceyrənin solundakı [12] kimi nömrə icra sırasını göstərir; nömrələr yuxarıdan aşağı artmırsa, notebook-un vəziyyəti ekranda görünəndən fərqlidir.

Bir neçə sadə vərdiş bu problemləri aradan qaldırır:

  • Nəticəni başqasına göstərmədən və ya commit etmədən əvvəl restart and run all edin. Notebook yalnız yuxarıdan aşağı təmiz icra olunanda etibarlıdır.
  • Exploration ilə təkrar istifadə olunan kodu ayırın. Bir neçə dəfə lazım olan funksiya .py faylına keçməlidir; notebook-da onu %run və ya import ilə çağırın. %autoreload extension-ı faylı dəyişəndə import-u avtomatik yeniləyir.
  • Hüceyrələri kiçik saxlayın: bir hüceyrə, bir fikir. Böyük hüceyrələr debug etməyi və yenidən icra etməyi çətinləşdirir.
  • Dəyişənləri yenidən təyin etməkdən çəkinin: df-i on dəfə üzərinə yazsanız, hansı df-in hansı vəziyyətdə olduğunu izləmək mümkün olmur.

Notebook exploration-ın alətidir, production kodunun yox. Fərqi bilmək həm notebook-dan, həm də koddan daha çox fayda almağa imkan verir.

Tapşırıqlar

  1. İstənilən kitabxanadan (məsələn, itertools) bir modul götürün və onun beş funksiyasını yalnız ?, ??, tab completion və wildcard axtarışı ilə öyrənin: sənədə və ya internetə baxmadan hər birinin nə etdiyini bir cümlə ilə yazın.
  2. 1 milyon ədədin kvadratlarının cəmini üç üsulla hesablayın: for loop, list comprehension və sum(map(...)). Hər birini %timeit ilə ölçün, sonra ən yavaşını %prun və %lprun ilə profile edib vaxtın harada getdiyini tapın.
  3. Köhnə bir notebook-unuzu (və ya kiməsə məxsus notebook-u) açın və "restart and run all" edin. Xəta çıxırsa, səbəbini icra sırası baxımından izah edin və notebook-u yuxarıdan aşağı təmiz işləyən hala gətirin.