پردازش زبان های غیر انگلیسی با پایتون — راهنمای کاربردی
در این مطلب، روش پردازش زبان های غیر انگلیسی با پایتون همراه با انجام یک مثال، مورد بررسی قرار گرفته است. در واقع، این نوشتار راهنمایی برای آخرین کتابخانه استنفورد، یعنی StanfordNLP است. برای درک بهتر مطلب، پیادهسازیهایی نیز بر اساس وظایف «پردازش زبان طبیعی» (Natural Language Processing | NLP)، در «زبان برنامهنویسی پایتون» (Python Programming Language) انجام شده است. در نهایت نیز یک بررسی موردی جالب، به عنوان مثالی از چگونگی پردازش زبانهای طبیعی غیر انگلیسی به طور کامل انجام شده است.
پردازش زبان های غیر انگلیسی با پایتون
یکی از چالشهای متداولی که افراد ضمن فراگیری پردازش زبان طبیعی با آن مواجه هستند، این است که آیا میتوانند برای زبانهای غیر انگلیسی مدل بسازند؟ پاسخ این پرسش تا چندی پیش «خیر» بود. نباید فراموش کرد که هر زبانی دارای الگوهای گرامری و تفاوتهای زبانی خاص خود است.
در اینجا است که پای آخرین کتابخانه پردازش زبان طبیعی استنفورد یعنی StanfordNLP به میان میآید. توسعهدهندگان این کتابخانه ادعا کردهاند که StanfordNLP از ۵۳ زبان زنده دنیا پشتیبانی میکند. StanfordNLP حاوی مدلهای از پیش آموزش داده شده برای زبانهای آسیایی مانند هندی، چینی و ژاپنی برای اسکریپتهای اصلی آنها است.
StanfordNLP چیست و چرا باید از آن استفاده کرد؟
StanfordNLP یک مجموعه از ابزارهای از پیش آموزش داده شده لبه علم است. این مدلها توسط پژوهشگران در رقابتهای سال ۲۰۱۷ و ۲۰۱۸ CoNLL مورد استفاده قرار گرفتند. همه مدلها بر پایه کتابخانه «پایتورچ» (PyTorch) ساخته شدهاند و قابل آموزش دادن و ارزیابی شدن روی دادههای مورد نظر کاربر هستند.
علاوه بر آن، StanfordNLP حاوی یک پوشش دهنده رسمی برای کتابخانه محبوب پردازش زبان طبیعی یعنی CoreNLP است. کتابخانه CoreNLP تاکنون محدود به اکوسیستم جاوا بوده است.
راهاندازی StanfordNLP در پایتون
چند نکته جالب پیرامون کتابخانه StanfordNLP وجود دارد که ممکن است کمی گیج کننده به نظر بیاید. برای مثال، یکی از این موارد نیاز به پایتون ۳.۶.۸/۳.۷.۲ یا جدیدتر برای استفاده همراه با StanfordNLP است. ضمن نوشتن این مطلب و برای اطمینان، یک محیط مجزا در «توزیع پایتون آناکوندا» (Anaconda Python Distribution) برای پایتون ۳.۷.۱ راهاندازی شده است. چگونگی انجام این کار، در ادامه شرح داده شده است.
۱. پرومت کوندا باز و دستور زیر در آن تایپ میشود.
1conda create -n stanfordnlp python=3.7.1
۲. اکنون، محیط باید فعال میشود.
1source activate stanfordnlp
۳. نصب کتابخانه StanfordNLP با استفاده از دستوری که در ادامه آمده است انجام میشود.
1pip install stanfordnlp
۴. نیاز به دانلود زبان خاص مدل برای کار کردن با آن است. در این راستا، یک شل پایتون راهاندازی و StanfordNLP وارد (ایمپورت | Import) میشود.
1import stanfordnlp
5. سپس، مدل زبانی برای انگلیسی «en» دانلود میشود.
1stanfordnlp.download('en')
این کار، بسته به سرعت اینترنت کاربر، کمی زمانبر است. باید توجه داشت که این مدلهای زبانی فوقالعاده زیاد هستند (نسخه انگلیسی ۱.۹۶ گیگابایت است).
نکات مهم پیرامون کار با کتابخانه StanfordNLP
StanfordNLP بر فراز PyTorch 1.0.0 ساخته شده است. بنابراین، در صورتی که کاربر نسخهای پایینتر از این را نصب داشته باشد، امکان از کار افتادن آن وجود دارد. در ادامه، کد لازم برای بررسی نسخهای از کتابخانه که روی سیستم کاربر نصب شده، بیان شده است.
1pip freeze | grep torch
خروجی قطعه کد بالا باید به صورت torch==1.0.0 باشد.
نکته: نویسنده این مطلب از کتابخانه StanfordNLP بدون GPU، روی لنوو ثینکپد E470 (هشت گیگ رم، گرافیک اینتل) استفاده کرده و به سرعت، با خطای حافظه در پایتون مواجه شده است. بنابراین، به ماشین با GPU فعال تغییر حالت داده و پیشنهاد میکند که دیگران نیز این کار را کنند. میتوان از گوگل کُلَب (Google Colab) استفاده کرد که با پشتیبانی رایگان از GPU را ارائه میدهد. در ادامه، به برخی از پردازشهای پایهای پردازش زبان طبیعی (NLP) پرداخته میشود.
استفاده از StanfordNLP برای انجام وظایف پایهای پردازش زبان طبیعی
۱. کار با ساخت یک پایپلاین متنی آغاز میشود.
1nlp = stanfordnlp.Pipeline(processors = "tokenize,mwt,lemma,pos")
2doc = nlp("""The prospects for Britain’s orderly withdrawal from the European Union on March 29 have receded further, even as MPs rallied to stop a no-deal scenario. An amendment to the draft bill on the termination of London’s membership of the bloc obliges Prime Minister Theresa May to renegotiate her withdrawal agreement with Brussels. A Tory backbencher’s proposal calls on the government to come up with alternatives to the Irish backstop, a central tenet of the deal Britain agreed with the rest of the EU.""")
آرگومان processors = “” برای تعیین یک وظیفه مورد استفاده قرار میگیرد. اگر هیچ آرگومانی پاس داده نشود، همه پنج پردازشگر به صورت پیشفرض گرفته میشوند.
توکنسازی
این فرایند به صورت ضمنی و هنگامی اتفاق میافتد که پردازشگر توکن در حال اجرا است. در واقع این کار به سرعت انجام میشود. میتوان با استفاده از print_tokens() نگاهی به توکنها داشت.
1doc.sentences[0].print_tokens()
شی توکن، حاوی اندیس توکن در جمله و یک لیست از اشیای کلمه است (در شرایطی که از توکن چند کلمهای استفاده شود). هر شی کلمه حاوی اطلاعات مفیدی مانند اندیس کلمه، lemma متن، تگ pos (اجزای کلام) و تگ feat (ویژگی صرفی) است.
Lemmatization
این مورد، شامل استفاده از خصوصیت «lemma» کلمات تولید شده به وسیله پردازشگر lemma است. در اینجا، کدی برای به دست آوردن lemma همه کلمات ارائه شده است.
1import pandas as pd
2
3def extract_lemma(doc):
4 parsed_text = {'word':[], 'lemma':[]}
5 for sent in doc.sentences:
6 for wrd in sent.words:
7 #extract text and lemma
8 parsed_text['word'].append(wrd.text)
9 parsed_text['lemma'].append(wrd.lemma)
10 #return a dataframe
11 return pd.DataFrame(parsed_text)
12extract_lemma(doc)
این کد، برای ارائه هر کلمه و lemma متعاقب آن در خروجی، از چارچوب داده «کتابخانه پانداس» (Pandas) استفاده میکند.
تگ اجزای کلمه (pos)
تگگذار PoS نسبتا سریع است و به خوبی در زبانهای گوناگون کار میکند. درست مانند lemmas، استخراج تگ pos نیز ساده است.
1#dictionary to hold pos tags and their explanations
2pos_dict = {
3'CC': 'coordinating conjunction',
4'CD': 'cardinal digit',
5'DT': 'determiner',
6'EX': 'existential there (like: \"there is\" ... think of it like \"there exists\")',
7'FW': 'foreign word',
8'IN': 'preposition/subordinating conjunction',
9'JJ': 'adjective \'big\'',
10'JJR': 'adjective, comparative \'bigger\'',
11'JJS': 'adjective, superlative \'biggest\'',
12'LS': 'list marker 1)',
13'MD': 'modal could, will',
14'NN': 'noun, singular \'desk\'',
15'NNS': 'noun plural \'desks\'',
16'NNP': 'proper noun, singular \'Harrison\'',
17'NNPS': 'proper noun, plural \'Americans\'',
18'PDT': 'predeterminer \'all the kids\'',
19'POS': 'possessive ending parent\'s',
20'PRP': 'personal pronoun I, he, she',
21'PRP$': 'possessive pronoun my, his, hers',
22'RB': 'adverb very, silently,',
23'RBR': 'adverb, comparative better',
24'RBS': 'adverb, superlative best',
25'RP': 'particle give up',
26'TO': 'to go \'to\' the store.',
27'UH': 'interjection errrrrrrrm',
28'VB': 'verb, base form take',
29'VBD': 'verb, past tense took',
30'VBG': 'verb, gerund/present participle taking',
31'VBN': 'verb, past participle taken',
32'VBP': 'verb, sing. present, non-3d take',
33'VBZ': 'verb, 3rd person sing. present takes',
34'WDT': 'wh-determiner which',
35'WP': 'wh-pronoun who, what',
36'WP$': 'possessive wh-pronoun whose',
37'WRB': 'wh-abverb where, when',
38'QF' : 'quantifier, bahut, thoda, kam (Hindi)',
39'VM' : 'main verb',
40'PSP' : 'postposition, common in indian langs',
41'DEM' : 'demonstrative, common in indian langs'
42}
43
44def extract_pos(doc):
45 parsed_text = {'word':[], 'pos':[], 'exp':[]}
46 for sent in doc.sentences:
47 for wrd in sent.words:
48 if wrd.pos in pos_dict.keys():
49 pos_exp = pos_dict[wrd.pos]
50 else:
51 pos_exp = 'NA'
52 parsed_text['word'].append(wrd.text)
53 parsed_text['pos'].append(wrd.pos)
54 parsed_text['exp'].append(pos_exp)
55 return pd.DataFrame(parsed_text)
56
57extract_pos(doc)
دیکشنری بزرگ استفاده شده در کد بالا، قابل توجه است. آنچه انجام شده صرفا یک نگاشت بین تگ PoS و معنای آنها است. این کار کمک میکند تا درک بهتری از ساختار syntactic مستندات به دست بیاید. خروجی یک چارچوب داده با سه ستون pos ،word و exp (توضیحات | explanation) است.
ستون توضیحات، اطلاعاتی پیرامون متن را ارائه میکند و بنابراین، بسیار مفید است. افزودن ستون توضیحات، ارزیابی اینکه عملکرد پردازشگر چقدر صحیح است را دشوار میکند. این حقیقت که تگگذار برای اکثریت کلمات on point است، اتفاق خوبی محسوب میشود. این تگ، حتی دهها کلمه را صرف نظر از اینکه به صورت پایه یا جمع هستند، انتخاب میکند.
استخراج وابستگی
استخراج ویژگی دیگر ویژگی جالب توجه StanfordNLP است. کاربر میتواند به سادگی print_dependencies() را روی جمله برای گرفتن ارتباطات وابستگیها برای همه کلمات فراهم کند.
1doc.sentences[0].print_dependencies()
کتابخانه همه کلمات بالا را در طول یک اجرای منفرد پایپلاین محاسبه میکند. این کار روی یک سیستم با GPU فعال، زمانی بسیار کم و در حد چند دقیقه از کاربر میگیرد. اکنون، مشخص میشود که چگونه پردازشهای متنی پایه با StanfordNLP انجام شود. به نظر میرسد که در این وهله، زمان استفاده از مزایای این قابلیت که میتوان از این کتابخانه برای ۵۱ زبان دیگر نیز استفاده کرد، فرا رسیده است.
پیادهسازی StanfordNLP روی زبان هندی
StanfordNLP حقیقتا به خاطر کارایی و تجزیه متن چند زبانه یکهتاز است. اکنون، نگاهی عمیقتر به این ویژگی کتابخانه مذکور انداخته میشود.
پردازش متن هندی (دیواناگری)
ابتدا، باید مدل زبان هندی را دانلود کرد (به طور قابل ملاحظهای کوچکتر است).
1stanfordnlp.download('hi')
اکنون، بخشی از یک متن هندی به عنوان مستندات متنی به مدل داده میشود.
1hindi_doc = nlp("""केंद्र की मोदी सरकार ने शुक्रवार को अपना अंतरिम बजट पेश किया. कार्यवाहक वित्त मंत्री पीयूष गोयल ने अपने बजट में किसान, मजदूर, करदाता, महिला वर्ग समेत हर किसी के लिए बंपर ऐलान किए. हालांकि, बजट के बाद भी टैक्स को लेकर काफी कन्फ्यूजन बना रहा. केंद्र सरकार के इस अंतरिम बजट क्या खास रहा और किसको क्या मिला, आसान भाषा में यहां समझें""")
این مورد برای تولید همه تگها کافی است. در ادامه، تگها برای زبان هندی مورد بررسی قرار میگیرند.
1extract_pos(hindi_doc)
تگ کننده PoS به طرز شگفتانگیزی روی متن هندی خوب کار میکند. برای مثال، میتوان «अपना» را در نظر داشت.
استفاده از رابط برنامهنویسی کاربردی CoreNLP’s برای تحلیل متن
CoreNLP یک جعبه ابزار آزموده شده و ابزار NLP سطح صنعتی است که به خاطر کارایی و صحت خود، شناخته شده است. StanfordNLP سه خط کد را برای شروع به استفاده از رابط برنامهنویسی کاربردی پیچیده CoreNLP’s نیاز دارد. به معنای واقعی کلمه، فقط و فقط به سه خط کد نیاز دارد.
۱. بسته CoreNLP باید دانلود شود. برای این کار، باید ترمینال لینوکس را باز و دستور زیر را وارد کرد.
1wget http://nlp.stanford.edu/software/stanford-corenlp-full-2018-10-05.zip
۲. بسته دانلود شده را باید از حالت زیپ خارج کرد.
1unzip stanford-corenlp-full-2018-10-05.zip
۳. CoreNLP باید شروع شود.
1java -mx4g -cp "*" edu.stanford.nlp.pipeline.StanfordCoreNLPServer -port 9000 -timeout 15000
نکته: CoreNLP برای اجرا شدن نیاز به Java8 دارد. باید اطمینان حاصل کرد که JDK و JRE 1.8.x نصب شدهاند.
اکنون، باید اطمینان حاصل کرد که StanfordNLP میداند که CoreNLP کجا ظاهر شده است. برای این کار، باید $CORENLP_HOME به عنوان موقعیت پوشه شما ثبت شود. در بسیاری از موارد، این پوشه در خود خانه وجود دارد، بنابراین، مسیر چیزی شبیه زیر خواهد بود.
1export CORENLP_HOME=stanford-corenlp-full-2018-10-05/
پس از انجام مراحل بالا، میتوان سرور را راهاندازی کرد و درخواستهایی را در کد پایتون داد. در ادامه، مثالی جامع از چگونگی راهاندازی یک سرور، ایجاد درخواست و دسترسی به دادهها از یک شی برگشت داده شده بیان شده است.
تنظیم CoreNLPClient
کد لازم برای تنظیم CoreNLPClient در ادامه آمده است.
1from stanfordnlp.server import CoreNLPClient
2# example text
3print('---')
4print('input text')
5print('')
6text = "Chris Manning is a nice person. Chris wrote a simple sentence. He also gives oranges to people."
7print(text)
8# set up the client
9print('---')
10print('starting up Java Stanford CoreNLP Server...')
11# set up the client
12with CoreNLPClient(annotators=['tokenize','ssplit','pos','lemma','ner','depparse','coref'], timeout=30000, memory='16G') as client:
13 # submit the request to the server
14 ann = client.annotate(text)
15 # get the first sentence
16 sentence = ann.sentence[0]
تجزیه وابستگیها و POS
1 #get the dependency parse of the first sentence
2 print('---')
3 print('dependency parse of first sentence')
4 dependency_parse = sentence.basicDependencies
5 print(dependency_parse)
6 # get the first token of the first sentence
7 print('---')
8 print('first token of first sentence')
9 token = sentence.token[0]
10 print(token)
11 # get the part-of-speech tag
12 print('---')
13 print('part of speech tag of token')
14 token.pos
15 print(token.pos)
تشخیص موجودیتهای نامدار و زنجیرههای همارجاع (Co-Reference)
1# get the named entity tag
2 print('---')
3 print('named entity tag of token')
4 print(token.ner)
5 # get an entity mention from the first sentence
6 print('---')
7 print('first entity mention in sentence')
8 print(sentence.mentions[0])
9 # access the coref chain
10 print('---')
11 print('coref chains for the example')
12 print(ann.corefChain)
13 # Use tokensregex patterns to find who wrote a sentence.
14 pattern = '([ner: PERSON]+) /wrote/ /an?/ []{0,3} /sentence|article/'
15 matches = client.tokensregex(text, pattern)
16 # sentences contains a list with matches for each sentence.
17 assert len(matches["sentences"]) == 3
18 # length tells you whether or not there are any matches in this
19 assert matches["sentences"][1]["length"] == 1
20 # You can access matches like most regex groups.
21 matches["sentences"][1]["0"]["text"] == "Chris wrote a simple sentence"
22 matches["sentences"][1]["0"]["1"]["text"] == "Chris"
23 # Use semgrex patterns to directly find who wrote what.
24 pattern = '{word:wrote} >nsubj {}=subject >dobj {}=object'
25 matches = client.semgrex(text, pattern)
26 # sentences contains a list with matches for each sentence.
27 assert len(matches["sentences"]) == 3
28 # length tells you whether or not there are any matches in this
29 assert matches["sentences"][1]["length"] == 1
30 # You can access matches like most regex groups.
31 matches["sentences"][1]["0"]["text"] == "wrote"
32 matches["sentences"][1]["0"]["$subject"]["text"] == "Chris"
33 matches["sentences"][1]["0"]["$object"]["text"] == "sentence"
سهولت استفاده و دسترسیپذیری بالا هنگام استفاده از CoreNLP در پایتون، بسیار جالب توجه است.
نکاتی پیرامون استفاده از StanfordNLP
چند نکته پیرامون کار با StanfordNLP وجود دارد که اشاره به آنها خالی از لطف نیست.
- کتابخانه StanfordNLP از چندین زبان پشتیبانی میکند.
- StanfordNLP رابط رسمی پایتون برای CoreNLP است و این یعنی، به مرور و با گذر زمان، کارکرد و سهولت استفاده از آن بهبود پیدا میکند.
- به طور مناسبی سریع است (مانع ردپای زیاد حافظه میشود).
- راهاندازی کتابخانه StanfordNLP در پایتون آسان است.
همچنین، در موارد زیر نیز به نظر میرسد که StanfordNLP نیازمند بهبود است.
- اندازه مدل زبانی آن بسیار بزرگ است (انگلیسی ۱.۹ گیگابایت، چینی ۱.۸ گیگابایت).
- کتابخانه نیازمند کد زیادی برای به دست آوردن ویژگیها است. این مورد با NLTK قابل مقایسه است که میتوان با استفاده از آن، یک نمونه اولیه را اسکریپتنویسی کرد. این کار احتمالا با StanfordNLP امکانپذیر نیست.
- از ویژگیهای بصریسازی نیز بهره نمیبرد. برای توابعی مانند تجزیه وابستگی، این ویژگی بسیار مفید است. متأسفانه باید گفت که در این زمینه، StanfordNLP، در مقایسه با کتابخانههای همچون SpaCy کم میآورد.
جمعبندی
به وضوح مشخص است که StanfordNLP در حال حاضر در مرحله بتا قرار دارد. البته که استفاده از آن بسیار مفید است. در حال حاضر، جعبه ابزارهای فوقالعادهای (CoreNLP) به اکوسیستم پایتون آمده و غولهای پژوهشی و توسعه مانند استنفورد نیز تلاشهای برای ارائه نرمافزارهای خود متنباز دارند و این موضوع به خودی خود، اتفاق مثبتی است.
اگر نوشته بالا برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای آمار، احتمالات و داده کاوی
- آموزشهای داده کاوی یا Data Mining در متلب
- مجموعه آموزشهای هوش محاسباتی
- آموزش پردازش زبان طبیعی پروژه محور — راهنمای کاربردی
- چگونه ۹۰ درصد مسائل پردازش زبان طبیعی (NLP) را حل کنیم؟ — راهنمای گام به گام
^^