Eski sürücüler roket kullanıyor! Cython, Python NLP'nin yüzlerce kat hızlanmasına yardımcı oluyor

Leifeng.com AI Araştırma Enstitüsü Not: Bu makalenin yazarı, Hugging face'ten bir bilim adamı olan Thomas Wolf'dur.Araştırma alanları arasında makine öğrenimi, doğal dil işleme ve derin öğrenme yer almaktadır. Bu blogda, Python'u doğal dil işleme görevlerinde yüzlerce kat daha hızlı hale getirmek için Cython ve spaCy'nin nasıl kullanılacağını tanıttı. Leifeng.com AI Araştırma Enstitüsü orijinal metni derledi.

SpaceX Falcon Heavy Launcher, telif hakkı SpaceX'e aittir

İpucu: Bu makaledeki tüm örnekler, bu Jupyter not defterinden kaynak kodunu alabilir.

Geçen yıl Python'da uygulanan Neural coreference çözüm paketini (Neural coreference çözüm paketi) yayınladıktan sonra, topluluktan şaşırtıcı miktarda geri bildirim aldık ve birçok kişi çözüm paketini çeşitli uygulamalarda kullanmaya başladı. İçinde, bazı uygulama senaryoları, başlangıçta tasarladığımız iletişim kutusu kullanım durumunu bile aştı.

Daha sonra, bu ayrıştırma paketinin ayrıştırma hızının iletişim mesajları için oldukça yeterli olmasına rağmen, daha büyük makaleleri ayrıştırmanın çok yavaş olduğunu keşfettik.

Bu yüzden çözümü derinlemesine keşfetmeye karar verdim ve sonunda NeuralCoref v3.0'ı geliştirdim. Bu sürüm, öncekinden yüz kat daha hızlıdır (saniyede birkaç bin kelimeyi analiz eder) ve yine de aynı doğruluğu sağlar.Elbette kullanımı kolaydır ve Python kütüphanesinin ekolojik ortamına uygundur.

Bu yazıda, NeuralCoref v3.0'ın geliştirilmesinde öğrendiğim bazı deneyimleri, özellikle aşağıdakilerle ilgili olarak sizlerle paylaşmak istiyorum:

  • Verimli bir modül tasarlamak için Python'u nasıl kullanabiliriz,

  • Süper verimli doğal dil işleme işlevleri tasarlamak için spaCy'nin yerleşik veri yapısından en iyi şekilde nasıl yararlanılır.

Başlığım aslında biraz hile yapıyor, çünkü aslında Python hakkında konuşacağım ve ayrıca bazı Cython özelliklerini tanıtacağım. Ama biliyor musun? Cython, Python'un bir üst kümesidir, bu yüzden korkutmasına izin vermeyin!

İpucu: Şu anda yazdığınız Python projesi zaten bir Cython projesidir.

İşte bu hızlandırma stratejisinin gerekli olabileceği bazı senaryolar:

  • Doğal dil işleme görevleri için uygulama düzeyinde bir modül geliştirmek için Python kullanıyorsunuz

  • Doğal bir dil işleme görevi için büyük bir veri kümesini analiz etmek için Python kullanıyorsunuz

  • PyTorch / TensoFlow gibi derin öğrenme çerçeveleri için büyük bir eğitim setini önceden işliyorsunuz veya derin öğrenme modeliniz, eğitim hızınızı ciddi şekilde yavaşlatan karmaşık işleme mantığına sahip bir toplu yükleyici kullanıyor

İpucu: Ayrıca bu makalede tartışılan tüm örnekleri içeren bir Jupyter not defteri yayınladım, indirip hata ayıklamaya hoş geldiniz!

Yüz kat hızlanmanın ilk adımı: kod analizi

Bilmeniz gereken ilk şey, kodunuzun çoğunun saf bir Python ortamında iyi çalışabileceğidir, ancak bazı darboğaz işlevleri vardır (Darboğazlar işlevleri). Onlara daha fazla "özen" verdikten sonra, Program birkaç büyüklük sırasına göre hızlandırılacaktır.

Bu yüzden, bu verimsiz modülleri bulmak için kendi Python kodunuzu analiz ederek başlamalısınız. Bir yol cProfile kullanmaktır:

cProfile içe aktar

pstat'ları içe aktar

my_slow_module'ümü içe aktar

cProfile.run ('my_slow_module.run', 'restats')

p = pstats.Stats ('restats')

p.sort_stats ('kümülatif'). print_stats (30)

Büyük olasılıkla, verimsizliğin nedeninin bazı döngü kontrollerinden kaynaklandığını göreceksiniz veya sinir ağlarını kullanırken çok fazla Numpy dizisi işlemi başlatmışsınızdır (Numpy'yi tanıtmak için burada zaman harcamayacağım, bu konu çok fazla makalede tartışıldı ).

Peki döngüyü nasıl hızlandırabiliriz?

Döngü hesaplamasını hızlandırmak için Pyhthon'da Cython ekleyin

Bu sorunu basit bir örnekle çözelim. Bir grup dikdörtgen olduğunu varsayarsak, bunları Python nesnelerinin bir listesi (Rectangle nesne örnekleri gibi) olarak saklarız. Modülümüzün ana işlevi, belirlenen eşikten kaç tane dikdörtgenin daha büyük olduğunu saymak için listede yinelemeli işlemler gerçekleştirmektir.

Python modülümüz çok basit:

rastgele ithalattan rastgele

sınıf Dikdörtgen:

def __init __ (öz, w, h):

self.w = w

self.h = h

def alanı (kendi):

dönüş self.w * self.h

def check_rectangles (dikdörtgenler, eşik):

n_out = 0

dikdörtgenlerde dikdörtgen için:

rectangle.area ise > eşik:

n_out + = 1

geri dön n_out

def main:

n_rectangles = 10000000

dikdörtgenler = liste (Aralıktaki i için dikdörtgen (rastgele, rasgele) (n_rectangles))

n_out = check_rectangles (dikdörtgenler, eşik = 0.25)

baskı (n_out)

Check_rectangles işlevi programımızın darboğazını oluşturur! Uzun bir Python nesneleri listesi üzerinde yinelenir ve bu işlem oldukça yavaş olacaktır, çünkü Python yorumlayıcısının her yinelemede çok fazla iş yapması gerekir (sınıfta alan yöntemini bulma, parametreleri paketleme ve açma, Python'u çağırma) API vb.).

Cython'dan döngü işlemini hızlandırmamıza yardım etmesini istemenin zamanı geldi.

Cython dili, Python'un bir üst kümesidir ve iki tür nesne içerir:

  • Python nesnesi Normal Python'da kullandığımız nesneler mi? Sayılar, dizeler, listeler ve sınıf örnekleri ve daha fazlası

  • Cython C nesneleri Bunlar C ve C ++ nesneleridir, örneğin Çift hassasiyet, tam sayı, kayan nokta, yapı ve vektör , Cython tarafından süper verimli düşük seviyeli dil kodunda derlenebilirler

Bu döngü Cython ile yeniden üretildiği sürece, daha yüksek yürütme hızına ulaşabilir, ancak Cython'da yalnızca Cython C nesnelerini değiştirebiliriz.

Bu döngüyü tanımlamanın en doğrudan yollarından biri, hesaplama sürecinde kullanmamız gereken tüm nesneleri içeren bir yapı tanımlamaktır. Özellikle, bu durumda dikdörtgenin uzunluğu ve genişliğidir.

Daha sonra dikdörtgen nesnelerin listesini C'nin yapı dizisinde saklayabilir ve ardından diziyi check_rectangles işlevine geçirebiliriz. Bu işlev artık girdi olarak bir C dizisi alacak.Ayrıca, işlevi bir Cython işlevi olarak tanımlamak için def yerine cdef anahtar sözcüğünü kullanıyoruz (not: cdef, Cython C nesnelerini tanımlamak için de kullanılabilir).

İşte modül programının Cython versiyonu:

from cymem.cymem cimport Havuzu

rastgele ithalattan rastgele

cdef struct Dikdörtgen:

yüzer w

yüzer h

cdef int check_rectangles (Dikdörtgen * dikdörtgenler, int n_rectangles, kayan eşik):

cdef int n_out = 0

# C dizileri boyut bilgisi içermez = > açıkça vermemiz gerekiyor

dikdörtgenlerde dikdörtgen için:

dikdörtgen ise .w * dikdörtgen .h > eşik:

n_out + = 1

geri dön n_out

def main:

cdef:

int n_rectangles = 10000000

şamandıra eşiği = 0.25

Havuz mem = Havuz

Dikdörtgen * dikdörtgenler =

aralıktaki i için (n_rectangles):

dikdörtgenler .w = rastgele

dikdörtgenler .h = rastgele

n_out = check_rectangles (dikdörtgenler, n_rectangles, eşik)

baskı (n_out)

Burada yerel C dizi işaretçisini kullanıyoruz, ancak başka seçenekleriniz de var, özellikle vektörler, ikili diziler ve kuyruklar gibi C ++ yapıları. Bu programda, cymem tarafından sağlanan, istenen C dizisi bellek alanını manuel olarak serbest bırakmayı önleyebilen bir Havuz bellek yönetimi nesnesi de kullandım. Havuzdaki nesneye artık ihtiyaç duyulmadığında, nesnenin kapladığı bellek alanını otomatik olarak serbest bırakacaktır.

Ek: spaCy API'sinin Cython standart sayfası, pratik uygulamalarda doğal dil işleme görevlerini uygulamak için Cython'u kullanmak için referans materyalleri sağlar.

Başlayalım!

Cython kodunu test etmenin, derlemenin ve yayınlamanın birçok yolu vardır. Cython, Python gibi doğrudan Jupyter Notebook'ta bile kullanılabilir.

Cython'u pip install cython komutu ile kurun.

Jupyter'de ilk test

Cython uzantısını Jupyter not defterine yüklemek için% load_ext Cython komutunu kullanın.

Sonra %% cython komutu ile Cython'u Jupyter not defterinde Python gibi kullanabiliriz.

Cython kodunu çalıştırırken derleme hatalarıyla karşılaşırsanız, lütfen Jupyter terminalinin tam çıktısını kontrol edin.

Çoğu durumda, %% cython'dan sonra - + etiketinin çıkarılması olabilir (örneğin, spaCy Cython arayüzünü kullandığınızda). Derleyici Numpy hakkında bir hata bildirirse, içeri aktarma numarası eksiktir.

Başta da belirttiğim gibi, lütfen bu Jupyter defterini okuyun ve bu makale Jupyter'de tartıştığımız tüm örnekleri içerir.

Cython kodunu yazın, kullanın ve yayınlayın

Cython kodunun dosya soneki .pyx'tir, bu dosyalar Cython derleyicisi tarafından C veya C ++ dosyalarına derlenecek ve daha sonra C derleyicisi tarafından bayt kodu dosyalarına derlenecektir. Sonunda Python yorumlayıcısı bu bayt kodu dosyalarını çağırabilecektir.

Bir .pyx dosyasını doğrudan bir Python programına yüklemek için pyximport'u da kullanabilirsiniz:

> > > ithal pyximport; pyximport.install

> > > my_cython_module'ü içe aktar

Ayrıca Cython kodunuzu bir Python paketi olarak oluşturabilir ve ardından normal bir Python paketi gibi içe aktarabilir veya yayınlayabilirsiniz.Daha fazla ayrıntı için lütfen buraya bakın. Ancak, bu yaklaşım, özellikle Cython paketini tüm platformlarda çalıştırmanız gerekiyorsa daha fazla zaman alır. Bir referans örneğine ihtiyacınız varsa, spaCy kurulum komut dosyasına da göz atabilirsiniz.

Doğal dil işleme görevlerini optimize etmeye başlamadan önce, hızlıca def, cdef ve cpdef olmak üzere üç anahtar kelimeyi tanıtalım. Cython'u kullanmayı öğrenmeye başlamadan önce ustalaşmanız gereken en önemli bilgiler bunlar.

Cython programlarında üç tür işlev kullanabilirsiniz:

  • Python fonksiyonları def anahtar sözcüğü ile tanımlanır ve girdisi ve çıktısı Python nesneleridir. İşlevde Python ve C / C ++ nesneleri kullanılabilir ve Cython ve Python işlevleri çağrılabilir.

  • Cython işlevleri, girdi nesneleri olarak kullanılabilen ve işlev içindeki Python ve C / C ++ nesnelerini çalıştırabilen veya çıkarabilen cdef anahtar sözcüğü ile tanımlanır. Bu işlevlere Python ortamından (yani, Python yorumlayıcısı ve Cython modüllerini içe aktarabilen diğer saf Python modülleri) erişilemez, ancak diğer Cython modülleri tarafından içe aktarılabilir.

  • Cpdef anahtar kelimesiyle tanımlanan Cython işlevi, cdef tarafından tanımlanan Cython işlevine çok benzer, ancak cpdef tarafından tanımlanan işlevler de Python dekoratörleri sağlar, böylece doğrudan Python ortamında çağrılabilirler (işlevler girdi ve çıktı olarak Python nesnelerini kullanır) Ek olarak, Cython modülünde çağrılması da desteklenir (işlev, girdi olarak C / C ++ veya Python nesnelerini alır).

Cdef anahtar kelimesinin başka bir kullanımı, kodda bir nesnenin Cython C / C ++ nesnesi olduğunu belirtmektir. Dolayısıyla, kodunuzdaki nesneleri bildirmek için cdef kullanmadığınız sürece, bu nesnelere yorumlayıcı tarafından Python nesneleri olarak davranılacaktır (bu, erişim hızını yavaşlatacaktır).

Doğal dil işlemeyi hızlandırmak için Cython ve spaCy kullanın

Bütün bunlar harika görünüyor, ancak ... doğal dil işleme görevlerini optimize etmeye bile başlamadık! Dize manipülasyonu, unicode kodlaması ve doğal dil işlemede kullandığımız bir darbe yoktur.

Ek olarak, Cython'un resmi belgeleri C dili türü dizeleri kullanmamanızı bile önerir:

Genel olarak konuşursak: Ne yaptığınızı tam olarak bilmiyorsanız, C tipi dizeler kullanmaktan kaçınmalı ve bunun yerine Python dize nesneleri kullanmalısınız.

Öyleyse dizeleri manipüle ederken, Cython'da nasıl daha verimli bir döngü tasarlarız?

spaCy dikkatimizi çekti.

spaCy'nin bu soruna yaklaşımı çok akıllıca.

Tüm dizeleri 64 bit karma kodlara dönüştürün

SpaCy'deki tüm unicode dizeleri (etiketli metin, küçük harfli metin, lemma, POS etiket etiketleri, ayrıştırma ağacı bağımlılık etiketleri, adlandırılmış varlık etiketleri vb.) StringStore adlı bir veride depolanır. Yapı, bir geçer 64 bit karma kod C tipi uint64_t gibi dizin.

StringStore nesnesi, Python unicode dizeleri ve 64 bit karma kodlar arasındaki arama eşlemesini uygular.

SpaCy'deki npl.vocab.strings, doc.vocab.strings veya span.doc.vocab.string gibi herhangi bir yerden ve herhangi bir nesneden erişilebilir.

Belirli bir modülün belirli belirteçlerde daha hızlı işlem hızı elde etmesi gerektiğinde, elde etmek için dize yerine C dili türü 64-bit karma kodu kullanabilirsiniz. StringStore arama tablosunu çağırmak, karma kodla ilişkili Python unicode dizesini döndürür.

Ancak spaCy bundan daha fazlasını yapabilir.Ayrıca, belgeler ve kelime dağarcığı ile dolu olan C dili tipi yapılara erişmemizi sağlar.Bu yapıları, kendi yapılarımızı inşa etmek zorunda kalmadan Cython döngülerinde kullanabiliriz.

SpaCy'nin dahili veri yapısı

SpaCy belgesiyle ilgili ana veri yapısı, işlenen dizenin simge sırasına ("sözcükler") ve TokenC yapısı olan doc.c adlı C dili türü nesnesindeki tüm ek açıklamalara sahip olan Doc nesnesidir. Dizi.

TokenC yapısı, her bir token hakkında ihtiyacımız olan tüm bilgileri içerir. Bu bilgiler, az önce gördüğümüz unicode dizesiyle yeniden ilişkilendirilebilen 64 bitlik bir karma kod olarak saklanır.

Bu güzel C yapılarının içeriğini doğru bir şekilde anlamak istiyorsanız, yeni oluşturulan spaCy'nin Cython API belgelerine bakabilirsiniz.

Ardından, basit bir doğal dil işleme örneğine bakalım.

Daha hızlı doğal dil işleme

Analiz edilmesi gereken bir metin dokümanı veri kümesi olduğunu varsayalım.

urllib.request'i içe aktar

ithal boşluk

yanıt olarak urllib.request.urlopen ('https://raw.githubusercontent.com/pytorch/examples/master/word_language_model/data/wikitext-2/valid.txt') ile:

text = response.read

nlp = spacy.load ('en')

doc_list = list (nlp (text.decode ('utf8')) aralıktaki i için (10))

Her biri yaklaşık 170.000 kelime içeren 10 belgeden oluşan bir liste oluşturmak için bir komut dosyası yazdım ve bunu analiz etmek için spaCy kullandım. Elbette 170.000 belgeyi de analiz edebiliriz (her belge 10 kelime içerir), ancak bu oluşturma sürecinin çok yavaş olmasına neden olur, bu yüzden yine de 10 belge seçtik.

Bu veri setinde belirli doğal dil işleme görevlerini genişletmek istiyoruz. Örneğin, veri kümesinde "run" kelimesinin kaç kez isim olarak göründüğünü sayabiliriz (örneğin, "NN" konuşma etiketinin bir parçası olarak spaCy tarafından işaretlenmiştir).

Yukarıdaki analiz sürecini uygulamak için Python döngülerini kullanmak çok basit ve sezgiseldir:

def slow_loop (doc_list, kelime, etiket):

n_out = 0

doc_list içindeki doc için:

doc için tok için:

tok.lower_ == word ve tok.tag_ == etiketi ise:

n_out + = 1

geri dön n_out

def main_nlp_slow (doc_list):

n_out = slow_loop (doc_list, 'çalıştır', 'NN')

baskı (n_out)

Ancak kodun bu sürümü çok yavaş çalışıyor! Cevabı almak için bu kodun dizüstü bilgisayarımda çalışması 1,4 saniye sürüyor. Veri setimiz milyonlarca belge içeriyorsa, yanıt almamız bir günden fazla sürebilir.

Hızlanma elde etmek için çoklu okumayı kullanabiliriz, ancak bu yaklaşım Python'da pek akıllıca değildir, çünkü global yorumlayıcı kilidi (GIL) ile de ilgilenmeniz gerekir. Ayrıca Cython'un çoklu okumayı da kullanabileceğini unutmayın! Cython, arka planda doğrudan OpenMP'yi arayabilir. Ancak burada paralelliği tartışacak vaktim yok, bu nedenle daha fazla ayrıntı için lütfen bu bağlantıyı kontrol edin.

Şimdi Python kodunu hızlandırmak için spaCy ve Cython'u kullanmayı deneyelim.

İlk olarak, veri yapısını göz önünde bulundurmalıyız Verileri saklamak için bir C-tipi diziye ve her belgenin TokenC dizisine bir göstericiye ihtiyacımız var. Ayrıca test karakterlerini ("çalıştır" ve "NN") 64 bitlik bir karma koda dönüştürmemiz gerekir.

İşlenmesi gereken tüm veriler C tipi bir nesne haline geldiğinde, veri kümesi üzerinde saf C dili hızında yineleme yapabiliriz.

İşte Cython ve spaCy'ye dönüştürülmüş bu örneğin uygulaması:

%% cython - +

import numpy # Bazen numpy'yi içe aktarmazsak numpy derleme hatası alırız

from cymem.cymem cimport Havuzu

spacy.tokens.doc adresinden cimport Doc

spacy.typedefs'ten cimport hash_t

spacy.structs'tan cimport TokenC

cdef struct DocElement:

TokenC * c

int uzunluk

cdef int fast_loop (DocElement * docs, int n_docs, hash_t word, hash_t etiketi):

cdef int n_out = 0

docs için:

doc.c'deki c için:

c.lex.lower == word ve c.tag == etiketi ise:

n_out + = 1

geri dön n_out

def main_nlp_fast (doc_list):

cdef int i, n_out, n_docs = len (doc_list)

cdef Havuz mem = Havuz

cdef DocElement * docs =

cdef Doc dokümanı

i için, numaralandırmadaki doc (doc_list): # Veritabanı yapımızı doldurun

dokümanlar .c = doc.c

dokümanlar .length = (

word_hash = doc.vocab.strings.add ('çalıştır')

tag_hash = doc.vocab.strings.add ('NN')

n_out = fast_loop (dokümanlar, n_docs, word_hash, tag_hash)

baskı (n_out)

Kod biraz uzun çünkü Cython işlevini çağırmadan önce main_nlp_fast içindeki C yapısını bildirip doldurmalıyız.

Ek: Kodunuzda düşük seviyeli yapıları birden çok kez kullanmanız gerekiyorsa, Python kodunu tasarlamak için C tipi yapının Cython genişletilmiş tip dekoratörünü kullanmak her seferinde C yapısını doldurmaktan daha zariftir. Bu, çoğu spaCy kodu tarafından benimsenen yapıdır.Yüksek verimlilik, düşük bellek maliyeti ve kolay erişim ile çok zariftir.

Bu kod dizisi uzamış olsa da, daha verimli çalışır! Jupyter dizüstü bilgisayarımda, bu Cython kodu yalnızca yaklaşık 20 milisaniye boyunca çalıştı, bu önceki saf Python döngüsünden yaklaşık 80 kat daha hızlı.

Jupyter dizüstü bilgisayar birimlerini kullanarak modül yazma hızı etkileyicidir ve doğal olarak diğer Python modülleri ve işlevlerine bağlanabilir: 20 milisaniyede yaklaşık 1,7 milyon kelime taranır, bu da saniyede 80 milyon kelimeye kadar işleyebileceğimiz anlamına gelir.

Bu, doğal dil işleme hızlandırma için Cython kullanmaya girişin sonudur, umarım herkes bundan hoşlanır.

Cython hakkında tanıtılabilecek birçok şey var, ancak bu makalenin asıl amacının ötesindedir (bu makale sadece bir giriş niteliğindedir). Şu andan itibaren, en iyi bilgi bu toparlama Cython öğreticisi ve spaCy doğal dil işleme hakkındaki Cython sayfası olabilir.

Daha fazla benzer içerik almak istiyorsanız, lütfen bizi sevmeyi unutmayın!

Leifeng.com AI Araştırma Enstitüsü tarafından derlenen Python'da 100 Kat Daha Hızlı Doğal Dil İşleme ile

Yeni nesil kenarlıksız teknoloji + özel şekilli tam ekran, Nubia'nın yeni amiral gemisi casus fotoğrafları çıktı!
önceki
Gerçek BAIC BJ80 spor otomobilin 2017 yılında piyasaya sürülmesinin beklendiği ortaya çıktı.
Sonraki
JY canlı stüdyosunda "entrikacı adam" ile karşılaşıldı Aceleyle azarlandı: Ne yapıyorsun? Bu maddenin patladığı söylenirse, patlar mı?
NOAH, yaz atmosferini böyle gösteren yeni Lookbook'u tekrar piyasaya sürüyor mu? !
Himalaya üyelerinin butiklerini ücretsiz dinleyebilen bir AI konuşmacısı Xiaoya Nano
LG, açıkça Çin markalarının Asya pazarının tamamını işgal ettiğini söyleyerek Çin pazarından çekilmek zorunda kaldı!
Bu yeşil taze! OPPO R9s Fresh Green resmen satışta!
Shancheng Patikasında bir "ünlü köpek" var. Binlerce turist her gün onunla fotoğraf çekmek için özel bir gezi yapıyor
Yeni Nazhijieyou 6 SUV veya PSA 1.6T motorun ortaya çıkması
Uzi netizenleri "hile" mi yapıyor? Kadın hayranlar çok sinirli ve sonuçları ciddi: erkeklerin hepsi büyük paça!
Snapdragon 845'in ilk sürümü hala Samsung. Gerçek makinenin videosu yayınlanıyor ve ekran-vücut oranı hayal gücünün ötesinde!
İPhone 8'in ön ve arka panel kısımları açığa çıkıyor Kabakta ne tür ilaçlar satılıyor? !
Dört süper pozitif A hissesini patlatıyor! İki şehir günlük 100 hisse limitini aşıyor, "müreffeh bahar pazarı" ne kadar ileri gidebilir?
300 yuan dümdüz aşağı! Snapdragon 835 + 8GB depolama alanı, fiyat Xiaomi Mi 6 ile senkronize edildi!
To Top