FastAI-ni daha sürətli etmək üçün necə

Fastai-nin daxili "paralel" funksiyası ilə əvvəlcədən işlənməni sürətləndirin

Paralel işləmə gücü. Marc-Olivier Jodoin'in Unsplash'daki şəkli

Hansı sözün danışıldığını anlamaq / proqnozlaşdırmaq üçün bir saniyəlik xam səs kliplərinin işlənməsini özündə cəmləşdirən Tensor Flow Speech Recognition Challenge-ı bitirən ilk Kaggle yarışmamda iştirak edirəm.

Mənim metodum şəkli təsnif etmədən əvvəl xam səsi spektroqramlara çevirməkdir. İşləyən və dəqiq bir model hazırlamışdım və test dəsti ilə sınadığımdan və test dəstində 150.000-dən çox WAV sənədinin olduğunu gördükdə ilk təqdimatımı etməkdən məmnun oldum. Hazırda 224 x 224 ölçüsündə təxminən 4 spektrogram / saniyə istehsal edirəm. Bu təxminən 10 saatdır, daha yaxşı bir yol olmalıdır.

Çox işləmə və çox işləmə dərhal ağlınıza gəlir, amma bunu necə edəcəyimi bilmirəm. Qorxunc görünürlər və boşqabım həqiqətən dolu olduqda daha çox öyrənmə tələb edirlər. Xoşbəxtlikdən fastai, Jeremy'nin 7-ci Dərsdə təsadüfən bəhs etdiyi bir "paralel" funksiyaya malikdir. Birinci addım həmişə sənədləri yoxlamaqdır.

sənəd (paralel)
Fastai.core.parallel üçün dəftər sənədləri (əsas sənədlər)

Sənədlərdə paralel olaraq göstərin [mənbə] [test]

Zəhmli, ola biləcəyi qədər sadə görünür. Paralel olaraq işləmək üçün bir funksiyanı və arqumentlər toplusunu həmin funksiyaya ötürürəm, qalanını fastai edir.

Paralelliyin oyuncaq nümunəsi

Aşağıdakı funksiya bir ədədi alır və kvadratı çap edir.

def print_square (num): çap (sayı ** 2)

Sonra bir siyahı anlama cəmini almaq istədiyimiz rəqəmlərin siyahısını ehtiva edən bir siyahı yarada bilərik.

num_list = [x üçün x üçün x (10)] paralel (print_square, num_list)

Bu kodu çalıştırın və görəcəksiniz:

Deyəsən əla işlədi, amma meydanlarımız haradadır? Onları çap etməliyik. Burada daha diqqətlə oxusan, nə baş verdiyini görəcəksən:

Fastai.core.parallel üçün tam sənədlər

"Func hər arr elementinin dəyərini və indeksini qəbul etməlidir"

Keçirdiyiniz funksiya müəyyən bir tip olmalıdır və yalnız iki arqument götürməlidir:

  1. Dəyər (arr nə ehtiva edir və funksiyanız üçün normal arqumentdir)
  2. Arrdakı dəyərin göstəricisi * (qeyd: funksiyanızın indekslə əlaqəli olması lazım deyil, ancaq funksiya tərifinə daxil edilməlidir)
* Sylvain Gugger, Fastai forumlarında mənə doğrulama görüntülərinin işləməsi üçün indeksin lazım olduğunu bildirdi. Eyni zamanda daxili istifadə üçün praktik bir funksiya da olmalıdır. Beləliklə, daha inkişaf etmişsinizsə, öz versiyanızı ProcessPoolExecution ilə tətbiq edə bilərsiniz

İndeksi qəbul etmək üçün funksiyamızı yenidən yazaq. Yenə də işimiz indekslə bir şey etmək məcburiyyətində deyil və hətta funksiya tərifində onu _ ilə əvəz edə bilərik.

def print_square (sayı, indeks): çap (sayı ** 2)

İndi də zəngimizi yenidən paralelləşdirməyə çalışırıq ...

Bu işləyir! Bir mübahisə aparan sadə bir işiniz varsa, işiniz bitdi. İndi fastai-nin paralel funksiyasından 2 ilə 10 qat daha sürətli çalışmaq üçün necə istifadə edəcəyinizi bilirsiniz! * İndeksi qəbul etmək üçün parametrləri dəyişdirin və funksiyanı arqumentlər toplusuna paralel olaraq ötürün. * Bu oyuncaq nümunəsidir və paralel olaraq işləmək təxminən 1000 qat daha yavaşdır. Aşağıda gerçək dünya göstəricilərinə sahib bir real dünya nümunəsidir

Daha real bir nümunədən istifadə edərək bunun praktikada necə göründüyü ...

Spektroqram yaratmaq və saxlamaq üçün mənim faktiki kodum budur. John Hartquist və Kajetan Olszewski TLDR tərəfindən hazırlanmış orijinal kod: WAV sənədini src_path / fname altında oxuyun, spektrogram yaradın və dst_path altında saxlayın.

def gen_spec (fname, src_path, dst_path): y, sr = librosa.load (src_path / fname) S = librosa.feature.melspectrogram (y, sr = sr, n_fft = 1024, hop_length = 512, n_mels = 128, güc = 1.0, fmin = 20, fmax = 8000) plt.figure (əncir = (2.24, 2.24)) pylab.axis ('off') pylab.axes ([0., 0., 1., 1.], frameon = False, xticks = [], yticks = []) librosa.display.specshow (librosa.power_to_db (S, ref = np.max), y_axis = 'mel', x_axis = 'time') save_path = f '{ dst_path / fname} .png 'pylab.savefig (save_path, bbox_inches = Yoxdur, pad_inches = 0, dpi = 100) pylab.close ()

Daha irəli getmədən əvvəl paralel üçün mənbə koduna nəzər salaq.

Fastai.core.parallel üçün mənbə kodu, göründüyü qədər qorxulu deyil

Qorxuducu görünür, amma həqiqətən burada baş verənlər, hər bir dəyəri arqumentlər toplusumuzdan əldə edib o-da saxladığımız və bu dəyərin indeksini i-də saxladığımızdan sonra func (o, i) çağırırıq. Bizim üçün bu o deməkdir ki, siyahıda göndərdiyimiz hər bir fname üçün paralel olaraq gen_spec (fname, index) çağırışları (bizim vəziyyətimizdə bir siyahı) və bizim üçün paralel işlənməni idarə edir ki, bu da daha sürətli işləmə ilə nəticələnir.

Bəs funksiyamız birdən çox arqument qəbul etdikdə nə edirik?

Gördüyünüz kimi, gen_spec funksiyamız üç arqument və paralel olaraq iki gözlənilən funksiyanı alır. Çözüm əlavə arqumentlərimizin həmişə bir fayl yolu və ya sabit ilə eyni olub-olmamasından və ya fərqli olub-olmamasından asılıdır.

A. Əlavə arqumentlər sabit / statikdirsə, standart dəyərlərlə yeni bir funksiya yaradın və ya Paralellər modelinə uyğun bir funksiya yaratmaq üçün Python Partial istifadə edin. Qismən istifadəyə üstünlük verirəm. Bunu aşağıda nümayiş etdirəcəyəm.

B. Hər dəfə funksiyanı çağırdığınız zaman dəyişən çoxsaylı mübahisələriniz varsa, onları bir arqument kimi verin və sonra paketdən çıxarın.

Çözüm A: 150.000 wav sənədimin hamısı eyni src_pathdadır və bütün spektrogramları eyni dst_path-a yerləşdirirəm, buna görə yalnız fname arqumenti dəyişir. Bura bir hissəni istifadə etmək üçün mükəmməl bir yerdir

Paralel, funksiya çağırışında indeksin adını açıq şəkildə göstərmədiyi üçün həmişə tərifimizdəki 2-ci arqument olmalıdır. Gəlin düzəldək.

def gen_spec (fname, index, src_path, dst_path):

Bundan sonra statik yollarımızdan keçərək yeni bir gen_spec_partial funksiyası yaradırıq

gen_spec_partial = qismən (gen_spec, src_path = path_audio, dst_path = path_spectrogram)

İşimiz bitdi. Həm gen_spec_partial, həm də paraleldən istifadə edərək 1000 spektrogram yaradaq və nə qədər çəkdiyini müqayisə edək.

Paralel olmadan 296 saniyə, paralel ilə 104 saniyə, demək olar ki, 3x daha sürətli.

Çözüm B: Əlavə dəlillərimiz statik deyilsə, son işimiz üçün indi nə edirik? Dəlillər toplusunu və indeksini qəbul etmək üçün funksiyamızı yenidən yazırıq və arqumentləri özündə cəmləşdirən topluca toplayırıq. Spektrogram nümunəmiz üçün belə görünür:

def gen_spec_parallel_tuple (arg_tuple, index): fname, src_path, dst_path = arg_tuple # Kodun qalan hissəsi eynidir və buraxılmışdır

Daha sonra ötürmək istədiyimiz bütün arqumentləri düzgün ölçülü küpə bağlayırıq və sonra gen_spec_parallel_tuple və arg_tuple_listimizi paralel olaraq ötürürük

Bu işləyir! Artıq əvvəlcədən işlənməni sürətləndirmək və məşqə daha çox vaxt sərf etmək üçün istənilən sayda arqumentlə funksiyaları necə icra edəcəyinizi və paralel olaraq necə icra edəcəyinizi bilirsiniz.